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

JavaScript로 두 개의 2차원 배열 행렬 곱셈 구현하기

이 글에서는 두 개의 2차원 숫자 배열(행렬)을 인자로 받아 행렬 곱셈 결과를 반환하는 JavaScript 함수를 작성하는 방법을 알아봅니다.

행렬 곱셈의 기본 원리

행렬 곱셈은 첫 번째 행렬의 열 개수와 두 번째 행렬의 행 개수가 같을 때만 가능합니다. 예를 들어 X×Z 크기의 행렬과 Z×Y 크기의 행렬을 곱하면 X×Y 크기의 결과 행렬이 만들어집니다.

먼저 예제로 사용할 두 개의 행렬을 살펴보겠습니다.

// 5 x 4 크기의 행렬 a
let a = [
    [1, 2, 3, 1],
    [4, 5, 6, 1],
    [7, 8, 9, 1],
    [1, 1, 1, 1],
    [5, 7, 2, 6]
];
// 4 x 6 크기의 행렬 b
let b = [
    [1, 4, 7, 3, 4, 6],
    [2, 5, 8, 7, 3, 2],
    [3, 6, 9, 6, 7, 8],
    [1, 1, 1, 2, 3, 6]
];

행렬 곱셈 함수 구현하기

아래 함수는 먼저 입력값이 유효한 2차원 배열인지 검사하고, 두 행렬의 차원이 곱셈 조건을 만족하는지 확인합니다. 이후 삼중 반복문을 사용해 각 위치의 값을 계산합니다.

const multiplyMatrices = (a, b) => {
    // 유효성 검사: 2차원 배열 형식인지 확인
    if (!Array.isArray(a) || !Array.isArray(b) || !a.length || !b.length) {
        throw new Error('인자는 2차원 배열 형식이어야 합니다');
    }
    let x = a.length,      // 첫 번째 행렬의 행 개수
        z = a[0].length,   // 첫 번째 행렬의 열 개수
        y = b[0].length;   // 두 번째 행렬의 열 개수
    if (b.length !== z) {
        // XxZ * ZxY => XxY 규칙 검증
        throw new Error('첫 번째 행렬의 열 개수는 두 번째 행렬의 행 개수와 같아야 합니다');
    }
    // 결과를 담을 x x y 크기의 0으로 초기화된 배열 생성
    let productRow = Array.apply(null, new Array(y)).map(Number.prototype.valueOf, 0);
    let product = new Array(x);
    for (let p = 0; p < x; p++) {
        product[p] = productRow.slice();
    }
    // 삼중 반복문으로 행렬 곱셈 수행
    for (let i = 0; i < x; i++) {
        for (let j = 0; j < y; j++) {
            for (let k = 0; k < z; k++) {
                product[i][j] += a[i][k] * b[k][j];
            }
        }
    }
    return product;
}
// 5 x 4 행렬
let a = [
    [1, 2, 3, 1],
    [4, 5, 6, 1],
    [7, 8, 9, 1],
    [1, 1, 1, 1],
    [5, 7, 2, 6]
];
// 4 x 6 행렬
let b = [
    [1, 4, 7, 3, 4, 6],
    [2, 5, 8, 7, 3, 2],
    [3, 6, 9, 6, 7, 8],
    [1, 1, 1, 2, 3, 6]
];
// 결과는 5 x 6 행렬이 됩니다
console.log(multiplyMatrices(a, b));

실행 결과

위 코드를 실행하면 콘솔에 다음과 같이 5×6 크기의 결과 행렬이 출력됩니다.

[
    [ 15, 33, 51, 37, 34, 40 ],
    [ 33, 78, 123, 85, 76, 88 ],
    [ 51, 123, 195, 133, 118, 136 ],
    [ 7, 16, 25, 18, 17, 22 ],
    [ 31, 73, 115, 88, 73, 96 ]
]

코드 설명

핵심 로직은 세 개의 중첩 반복문입니다. 외부의 두 반복문(i, j)은 결과 행렬의 각 좌표를 순회하고, 내부 반복문(k)은 해당 좌표의 값을 계산하기 위해 첫 번째 행렬의 i번째 행과 두 번째 행렬의 j번째 열의 요소들을 곱한 뒤 모두 더합니다.

또한 이 함수는 다음과 같은 안정성 장점을 가집니다.

- 입력값 검증: 인자가 배열이 아니거나 비어 있으면 에러를 발생시켜 잘못된 호출을 조기에 잡아냅니다.
- 차원 호환 검사: 곱셈이 수학적으로 불가능한 경우 명확한 에러 메시지로 알려줍니다.
- 독립적인 초기화: productRow.slice()를 사용해 각 행을 별도의 배열로 복제하므로, 참조 공유로 인한 값 오염 문제를 방지합니다.

이 방식의 시간 복잡도는 O(x·y·z)로, 일반적인 행렬 곱셈 알고리즘과 동일합니다. 작은 규모의 행렬 연산에는 충분히 효율적이며, 그래픽 변환이나 데이터 처리 등 다양한 상황에서 활용할 수 있습니다.