두 개의 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³) 행렬 곱셈 알고리즘에 해당합니다. 그래픽스 변환, 머신러닝 등 행렬 연산이 필요한 다양한 분야에서 이 코드를 응용해 활용할 수 있습니다.