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

JavaScript로 구현하는 행렬 곱셈 알고리즘 완벽 가이드

두 개의 2차원 숫자 배열(행렬)을 입력받아 행렬 곱셈 결과를 반환하는 JavaScript 함수를 작성해 보겠습니다. 이 글에서는 행렬 곱셈의 기본 원리부터 실제 코드 구현, 그리고 실행 결과까지 단계별로 자세히 살펴봅니다.

행렬 곱셈의 기본 규칙

행렬 곱셈이 성립하려면 중요한 조건이 하나 있습니다. 바로 첫 번째 행렬의 열(column) 개수와 두 번째 행렬의 행(row) 개수가 같아야 한다는 것입니다.

예를 들어, X×Z 크기의 행렬 A와 Z×Y 크기의 행렬 B를 곱하면 그 결과는 X×Y 크기의 행렬이 됩니다. 결과 행렬의 각 요소는 다음 공식으로 계산됩니다.

result[i][j] = Σ (a[i][k] × b[k][j])  (k = 0부터 z-1까지)

구현 코드

입력값 검증, 차원 확인, 삼중 반복문을 활용한 전체 구현 코드는 다음과 같습니다.

const multiplyMatrices = (a, b) => {
   // 입력값이 유효한 2차원 배열인지 검증
   if (!Array.isArray(a) || !Array.isArray(b) || !a.length || !b.length) {
      throw new Error('arguments should be in 2-dimensional array format');
   }
   let x = a.length,   // 첫 번째 행렬의 행 개수
   z = a[0].length,    // 첫 번째 행렬의 열 개수
   y = b[0].length;    // 두 번째 행렬의 열 개수
   if (b.length !== z) {
      // XxZ & ZxY => XxY : 곱셈 조건 확인
      throw new Error('number of columns in the first matrix should be
      the same as the number of rows in the second');
   }
   // 결과 행렬을 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 크기의 행렬 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]
];
// 결과는 5 x 6 크기의 행렬이 됩니다.
console.log(multiplyMatrices(a, b));

코드 동작 원리

위 코드의 핵심 로직을 단계별로 정리하면 다음과 같습니다.

1. 입력값 검증

두 인자가 모두 배열이고 비어 있지 않은지 먼저 확인합니다. 조건에 맞지 않으면 에러를 발생시켜 잘못된 입력을 사전에 차단합니다.

2. 차원 추출 및 호환성 확인

첫 번째 행렬의 행 개수(x), 열 개수(z), 두 번째 행렬의 열 개수(y)를 추출합니다. 이때 두 번째 행렬의 행 개수(b.length)가 첫 번째 행렬의 열 개수(z)와 일치하지 않으면 곱셈이 불가능하므로 에러를 던집니다.

3. 결과 행렬 초기화

x×y 크기의 결과 행렬을 미리 생성하고 모든 값을 0으로 채워 둡니다. 이후 계산된 값들이 누적될 수 있도록 준비하는 과정입니다.

4. 삼중 반복문으로 곱셈 수행

i, j, k 세 개의 인덱스를 사용하여 첫 번째 행렬의 i번째 행과 두 번째 행렬의 j번째 열의 각 요소들을 곱한 뒤 모두 더해 결과 행렬의 [i][j] 위치에 저장합니다.

실행 결과

위 예제 코드를 실행하면 콘솔에 다음과 같이 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 ]
]

마무리

이처럼 JavaScript에서는 삼중 반복문만으로도 행렬 곱셈을 손쉽게 구현할 수 있습니다. 시간 복잡도는 O(x·y·z)로, 일반적인 O(n³) 행렬 곱셈 알고리즘에 해당합니다. 그래픽스 변환, 머신러닝 등 행렬 연산이 필요한 다양한 분야에서 이 코드를 응용해 활용할 수 있습니다.