Computer >> 컴퓨터 >  >> 프로그래밍 >> C 프로그래밍

C 언어로 배우는 행렬 곱셈과 행렬 정규화: 개념부터 구현까지

행렬 곱셈의 조건

두 행렬의 곱셈은 모든 경우에 가능한 것이 아니라, 반드시 특정 조건을 만족해야만 수행할 수 있습니다. 두 행렬 P와 Q가 있고, 각각의 차원이 P(a × b), Q(z × y)라고 가정해 보겠습니다. 이때 두 행렬의 곱셈이 성립하려면 P의 열 개수(b)와 Q의 행 개수(z)가 반드시 같아야 합니다. 조건이 만족되면 결과 행렬 R의 크기는 (a × y)가 됩니다.

행렬 곱셈 알고리즘

위 조건을 바탕으로 한 행렬 곱셈의 의사 코드(pseudocode)는 다음과 같습니다.

matrixMultiply(P, Q):
P의 차원은 (a × b), Q의 차원은 (z × y)라고 가정
시작
    만약 b ≠ z 라면 프로그램 종료
    그렇지 않으면 결과 행렬 R을 (a × y) 크기로 정의
    i를 0부터 a-1까지 반복:
        j를 0부터 y-1까지 반복:
            R[i][j] = 0 으로 초기화
            k를 0부터 z-1까지 반복:
                R[i][j] = R[i][j] + (P[i][k] * Q[k][j])
종료

핵심은 세 겹의 반복문입니다. 바깥쪽 두 반복문이 결과 행렬의 각 위치(i, j)를 순회하고, 가장 안쪽 반복문이 해당 위치의 값을 계산하기 위해 필요한 곱셈과 덧셈을 수행합니다.

행렬 정규화란?

행렬 정규화(matrix normalization)는 각 행(row)을 하나의 벡터로 보고, 그 벡터의 길이(크기)가 1이 되도록 만드는 과정입니다. 구체적으로는 각 요소를 해당 행 전체 제곱합의 제곱근으로 나누면 됩니다.

예를 들어 다음과 같은 2×3 행렬이 있다고 가정해 보겠습니다.

4 5 6
1 2 3

첫 번째 행의 제곱합은 4² + 5² + 6² = 77이므로 √77로 나누고, 두 번째 행의 제곱합은 1² + 2² + 3² = 14이므로 √14로 나눕니다. 따라서 정규화된 행렬은 다음과 같습니다.

4/√77   5/√77   6/√77
1/√14   2/√14   3/√14

C 언어 전체 구현 예제

아래는 사용자로부터 행렬을 입력받아 각 행의 제곱합을 계산한 뒤, 정규화된 행렬을 출력하는 완전한 C 프로그램입니다.

#include <stdio.h>
#include <math.h>

int main() {
    int row, col, i, j;
    int matrix[50][50] = {0};
    long long square[50] = {0};
    double norm[50][50], k;

    printf("행렬의 크기를 입력하세요: ");
    scanf("%d %d", &row, &col);

    printf("%dX%d 크기의 행렬 요소를 입력하세요:\n", row, col);
    for (i = 0; i < row; i++) {
        for (j = 0; j < col; j++) {
            scanf("%d", &matrix[i][j]);
        }
    }

    printf("\n입력된 행렬 (%d행 %d열):\n", row, col);
    for (i = 0; i < row; i++) {
        for (j = 0; j < col; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }

    /* 각 행의 제곱합 계산 */
    for (i = 0; i < row; i++) {
        for (j = 0; j < col; j++) {
            square[i] += (long long)matrix[i][j] * matrix[i][j];
        }
        printf("%d번째 행의 제곱합: %lld\n", i, square[i]);
    }

    /* 행렬 정규화 */
    for (i = 0; i < row; i++) {
        k = 1.0 / sqrt((double)square[i]);
        for (j = 0; j < col; j++) {
            norm[i][j] = k * matrix[i][j];
        }
    }

    printf("\n정규화된 행렬:\n");
    for (i = 0; i < row; i++) {
        for (j = 0; j < col; j++) {
            printf("%.3lf ", norm[i][j]);
        }
        printf("\n");
    }

    return 0;
}

실행 결과

행렬의 크기를 입력하세요: 2 3
2X3 크기의 행렬 요소를 입력하세요:
4 5 6
1 2 3

입력된 행렬 (2행 3열):
4 5 6
1 2 3
0번째 행의 제곱합: 77
1번째 행의 제곱합: 14

정규화된 행렬:
0.456 0.570 0.684
0.267 0.535 0.802

정리

이 예제에서 확인할 수 있듯이, 정규화된 각 행의 요소를 다시 제곱하여 더하면 약 1에 가까운 값이 나옵니다. 즉, 각 행이 길이 1인 단위 벡터(unit vector)로 변환된 것입니다. 이러한 정규화 기법은 머신러닝의 특성 스케일링, 컴퓨터 그래픽스의 방향 벡터 처리 등 다양한 분야에서 널리 활용되므로, 행렬 곱셈의 기본 원리와 함께 꼭 익혀두는 것이 좋습니다.