들어가며
행렬(Matrix)은 그래픽 처리, 데이터 분석, 머신러닝, 게임 개발 등에 쓰이며 코딩 테스트에도 등장합니다. 코드에서는 주로 2차원 배열로 표현하므로 행과 열을 순회하는 연습에도 알맞습니다. 이 글에서는 행렬의 종류와 기본 연산을 정리한 뒤 JavaScript로 행렬 곱셈을 구현합니다.
기본 용어
행렬(matrix)은 숫자 또는 문자를 직사각형 형태로 배열한 것입니다.
가로줄을 행(row) , 세로줄을 열(column) 이라고 합니다.
예시:
[ 1 3 2 5 1 7 ] \begin{bmatrix}
1 & 3 & 2 \\
5 & 1 & 7
\end{bmatrix} [ 1 5 3 1 2 7 ]
위 행렬은 2 × 3 2 \times 3 2 × 3 행렬이라고 하며, 7 7 7 은 ( 2 , 3 ) (2,3) ( 2 , 3 ) 성분입니다.
행렬 표기
행렬은 일반적으로 다음과 같이 나타냅니다.
A = [ a 11 a 12 a 13 a 21 a 22 a 23 ] A = \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \end{bmatrix} A = [ a 11 a 21 a 12 a 22 a 13 a 23 ]
각 성분은 a i j a_{ij} a ij 형태로 표기합니다.
[ 3 5 − 1 7 ] \begin{bmatrix} 3 & 5 \\ -1 & 7 \end{bmatrix} [ 3 − 1 5 7 ]
위 행렬은 2 × 2 2 \times 2 2 × 2 행렬 또는 2차 정사각 행렬(square matrix) 입니다.
행렬의 종류
전치 행렬 (Transpose Matrix)
전치 행렬(transpose matrix)은 원래 행렬의 행과 열을 바꾼 행렬입니다. 보통 A ⊤ A^{\top} A ⊤ 또는 A T A^T A T 로 표기합니다.
예시:
A = [ 1 2 4 5 3 7 ] ⇒ A T = [ 1 5 2 3 4 7 ] A = \begin{bmatrix} 1 & 2 & 4 \\ 5 & 3 & 7 \end{bmatrix} \Rightarrow A^T = \begin{bmatrix} 1 & 5 \\ 2 & 3 \\ 4 & 7 \end{bmatrix} A = [ 1 5 2 3 4 7 ] ⇒ A T = 1 2 4 5 3 7
전치 행렬은 열벡터를 행벡터로 표현하거나, 행과 열의 배치를 연산에 맞게 바꿀 때 사용합니다.
대칭 행렬 (Symmetric Matrix)
전치 행렬이 자기 자신과 같은 행렬을 대칭 행렬이라고 합니다: A = A ⊤ A = A^{\top} A = A ⊤
A = [ 1 3 3 a ] ⇒ A T = [ 1 3 3 a ] A = \begin{bmatrix} 1 & 3 \\ 3 & a \end{bmatrix} \Rightarrow A^T = \begin{bmatrix} 1 & 3 \\ 3 & a \end{bmatrix} A = [ 1 3 3 a ] ⇒ A T = [ 1 3 3 a ]
대칭 행렬은 그래프 이론이나 공학에서 자주 사용되며, 물리학에서도 응력(stress)과 관련된 행렬로 활용됩니다.
대각 행렬 (Diagonal Matrix)
정사각 행렬 중 대각 성분이 아닌 모든 성분이 0 0 0 인 행렬을 대각 행렬(diagonal matrix) 이라고 합니다.
[ 1 0 0 0 0 0 0 0 3 ] \begin{bmatrix} 1 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 3 \end{bmatrix} 1 0 0 0 0 0 0 0 3
대각 성분은 0 0 0 이어도 상관없습니다.
대각 행렬은 항상 대칭 행렬입니다.
대각 원소만 저장하거나 계산하는 구현에서는 연산량과 저장 공간을 줄일 수 있습니다.
단위 행렬 (Identity Matrix)
대각 성분만 1 1 1 이고 나머지는 0 0 0 인 행렬을 단위 행렬(identity matrix) 이라고 합니다. 단위 행렬 I I I 는 행렬 곱셈에서 곱셈 항등원의 역할을 합니다.
I = [ 1 0 0 1 ] , I = [ 1 0 0 0 1 0 0 0 1 ] I = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}, \quad I = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} I = [ 1 0 0 1 ] , I = 1 0 0 0 1 0 0 0 1
A ⋅ I n = I m ⋅ A = A ( A 는 m × n 행렬 ) A \cdot I_n = I_m \cdot A = A \quad (A \text{는 } m \times n \text{ 행렬}) A ⋅ I n = I m ⋅ A = A ( A 는 m × n 행렬 )
이는 일반적인 수에서 1 1 1 이 곱셈의 항등원 역할을 하는 것과 같은 개념입니다.
영행렬 (Zero Matrix)
모든 성분이 0 0 0 인 행렬을 영행렬(zero matrix) 이라고 합니다.
[ 0 0 0 0 ] , [ 0 0 0 0 0 0 0 0 0 ] \begin{bmatrix} 0 & 0 \\ 0 & 0 \end{bmatrix}, \quad \begin{bmatrix} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \end{bmatrix} [ 0 0 0 0 ] , 0 0 0 0 0 0 0 0 0
영행렬은 행렬 덧셈의 항등원입니다. 크기가 같은 행렬에 영행렬을 더해도 원래 행렬이 그대로 남습니다.
행렬의 연산
행렬의 비교: A = B A = B A = B
두 행렬의 크기가 같고 같은 위치의 성분이 모두 같을 때 A = B A = B A = B 입니다.
[ 1 x 2 3 ] = [ 1 2 2 y ] \begin{bmatrix} 1 & x \\ 2 & 3 \end{bmatrix} = \begin{bmatrix} 1 & 2 \\ 2 & y \end{bmatrix} [ 1 2 x 3 ] = [ 1 2 2 y ]
이때, x = 2 , y = 3 x = 2, y = 3 x = 2 , y = 3 입니다.
행렬의 실수배 (Scalar Multiplication)
행렬의 모든 성분에 특정한 숫자(스칼라)를 곱하는 연산입니다. 아래 실수배와 덧셈·뺄셈 예제에서는 다음 두 행렬을 사용합니다.
A = [ 2 1 5 3 ] , B = [ − 1 4 1 2 ] A = \begin{bmatrix} 2 & 1 \\ 5 & 3 \end{bmatrix}, \quad B = \begin{bmatrix} -1 & 4 \\ 1 & 2 \end{bmatrix} A = [ 2 5 1 3 ] , B = [ − 1 1 4 2 ]
3 A = [ 6 3 15 9 ] 3A = \begin{bmatrix} 6 & 3 \\ 15 & 9 \end{bmatrix} 3 A = [ 6 15 3 9 ]
행렬의 덧셈 (Addition)과 뺄셈 (Subtraction)
같은 크기의 행렬끼리만 가능합니다.
A + B = [ 1 5 6 5 ] A + B = \begin{bmatrix} 1 & 5 \\ 6 & 5 \end{bmatrix} A + B = [ 1 6 5 5 ]
A − B = [ 3 − 3 4 1 ] A - B = \begin{bmatrix} 3 & -3 \\ 4 & 1 \end{bmatrix} A − B = [ 3 4 − 3 1 ]
행렬의 곱셈 (Matrix Multiplication)
행렬 곱셈은 일반적인 덧셈이나 뺄셈과 다릅니다. 같은 위치에 있는 성분끼리 연산하는 것이 아니라, 왼쪽 행렬의 행과 오른쪽 행렬의 열을 대응시켜 계산 합니다.
예제 1: 2 × 2 2 \times 2 2 × 2 행렬 곱셈
[ 1 2 3 4 ] × [ 5 6 7 8 ] \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} \times \begin{bmatrix} 5 & 6 \\ 7 & 8 \end{bmatrix} [ 1 3 2 4 ] × [ 5 7 6 8 ]
각 행과 열의 성분을 곱하고 합하여 새로운 행렬을 만듭니다.
= [ ( 1 ⋅ 5 + 2 ⋅ 7 ) ( 1 ⋅ 6 + 2 ⋅ 8 ) ( 3 ⋅ 5 + 4 ⋅ 7 ) ( 3 ⋅ 6 + 4 ⋅ 8 ) ] = \begin{bmatrix} (1 \cdot 5 + 2 \cdot 7) & (1 \cdot 6 + 2 \cdot 8) \\ (3 \cdot 5 + 4 \cdot 7) & (3 \cdot 6 + 4 \cdot 8) \end{bmatrix} = [ ( 1 ⋅ 5 + 2 ⋅ 7 ) ( 3 ⋅ 5 + 4 ⋅ 7 ) ( 1 ⋅ 6 + 2 ⋅ 8 ) ( 3 ⋅ 6 + 4 ⋅ 8 ) ]
= [ 19 22 43 50 ] = \begin{bmatrix} 19 & 22 \\ 43 & 50 \end{bmatrix} = [ 19 43 22 50 ]
위 연산에서 결과 행렬의 각 성분은 왼쪽 행렬의 행과 오른쪽 행렬의 열을 곱한 값들의 합 입니다.
예제 2: 2 × 2 2 \times 2 2 × 2 행렬과 2 × 1 2 \times 1 2 × 1 행렬의 곱셈
[ 1 2 3 4 ] × [ 5 6 ] \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} \times \begin{bmatrix} 5 \\ 6 \end{bmatrix} [ 1 3 2 4 ] × [ 5 6 ]
이 경우, 결과 행렬의 크기는 ( 2 × 1 ) (2 \times 1) ( 2 × 1 ) 이 됩니다.
= [ ( 1 ⋅ 5 + 2 ⋅ 6 ) ( 3 ⋅ 5 + 4 ⋅ 6 ) ] = \begin{bmatrix} (1 \cdot 5 + 2 \cdot 6) \\ (3 \cdot 5 + 4 \cdot 6) \end{bmatrix} = [ ( 1 ⋅ 5 + 2 ⋅ 6 ) ( 3 ⋅ 5 + 4 ⋅ 6 ) ]
= [ 17 39 ] = \begin{bmatrix} 17 \\ 39 \end{bmatrix} = [ 17 39 ]
예제 3: 2 × 3 2 \times 3 2 × 3 행렬과 3 × 2 3 \times 2 3 × 2 행렬의 곱셈
[ 4 − 2 1 0 2 3 ] × [ 0 − 3 − 2 1 1 4 ] \begin{bmatrix}
4&-2&1\\
0&2&3\\
\end{bmatrix}
\times
\begin{bmatrix}
0&-3\\
-2&1\\
1&4\\
\end{bmatrix} [ 4 0 − 2 2 1 3 ] × 0 − 2 1 − 3 1 4
= [ ( 4 ⋅ 0 + ( − 2 ) ⋅ ( − 2 ) + 1 ⋅ 1 ) ( 4 ⋅ ( − 3 ) + ( − 2 ) ⋅ 1 + 1 ⋅ 4 ) ( 0 ⋅ 0 + 2 ⋅ ( − 2 ) + 3 ⋅ 1 ) ( 0 ⋅ ( − 3 ) + 2 ⋅ 1 + 3 ⋅ 4 ) ] = \begin{bmatrix}
(4 \cdot 0+(-2) \cdot (-2)+1 \cdot 1) & (4 \cdot (-3)+(-2) \cdot 1+1 \cdot 4) \\
(0 \cdot 0+2 \cdot (-2)+3 \cdot 1) & (0 \cdot (-3)+2 \cdot 1+3 \cdot 4) \\
\end{bmatrix} = [ ( 4 ⋅ 0 + ( − 2 ) ⋅ ( − 2 ) + 1 ⋅ 1 ) ( 0 ⋅ 0 + 2 ⋅ ( − 2 ) + 3 ⋅ 1 ) ( 4 ⋅ ( − 3 ) + ( − 2 ) ⋅ 1 + 1 ⋅ 4 ) ( 0 ⋅ ( − 3 ) + 2 ⋅ 1 + 3 ⋅ 4 ) ]
= [ 5 − 10 − 1 14 ] = \begin{bmatrix}
5&-10\\
-1&14\\
\end{bmatrix} = [ 5 − 1 − 10 14 ]
설명:
첫 번째 행 과 첫 번째 열 을 곱한 값이 첫 번째 행 , 첫 번째 열 의 성분이 됩니다.
C 11 = A 11 ⋅ B 11 + A 12 ⋅ B 21 + A 13 ⋅ B 31 C_{11} = A_{11} \cdot B_{11} + A_{12} \cdot B_{21} + A_{13} \cdot B_{31} C 11 = A 11 ⋅ B 11 + A 12 ⋅ B 21 + A 13 ⋅ B 31
첫 번째 행 과 두 번째 열 을 곱한 값이 첫 번째 행 , 두 번째 열 의 성분이 됩니다.
C 12 = A 11 ⋅ B 12 + A 12 ⋅ B 22 + A 13 ⋅ B 32 C_{12} = A_{11} \cdot B_{12} + A_{12} \cdot B_{22} + A_{13} \cdot B_{32} C 12 = A 11 ⋅ B 12 + A 12 ⋅ B 22 + A 13 ⋅ B 32
두 번째 행 과 첫 번째 열 을 곱한 값이 두 번째 행 , 첫 번째 열 의 성분이 됩니다.
C 21 = A 21 ⋅ B 11 + A 22 ⋅ B 21 + A 23 ⋅ B 31 C_{21} = A_{21} \cdot B_{11} + A_{22} \cdot B_{21} + A_{23} \cdot B_{31} C 21 = A 21 ⋅ B 11 + A 22 ⋅ B 21 + A 23 ⋅ B 31
두 번째 행 과 두 번째 열 을 곱한 값이 두 번째 행 , 두 번째 열 의 성분이 됩니다.
C 22 = A 21 ⋅ B 12 + A 22 ⋅ B 22 + A 23 ⋅ B 32 C_{22} = A_{21} \cdot B_{12} + A_{22} \cdot B_{22} + A_{23} \cdot B_{32} C 22 = A 21 ⋅ B 12 + A 22 ⋅ B 22 + A 23 ⋅ B 32
이처럼 각 행과 열을 대응하여 곱하고 더하는 방식 으로 행렬의 곱셈을 수행합니다.
행렬 곱셈의 일반적인 규칙
행렬 곱셈이 성립하려면 왼쪽 행렬의 열 개수와 오른쪽 행렬의 행 개수가 같아야 합니다.
결과 행렬의 크기는 왼쪽 행렬의 행 개수 × 오른쪽 행렬의 열 개수 가 됩니다.
즉, 다음과 같은 수식을 만족해야 합니다.
A ( m × n ) × B ( n × r ) = C ( m × r ) A (m \times n) \times B (n \times r) = C (m \times r) A ( m × n ) × B ( n × r ) = C ( m × r )
이 규칙을 코드로 옮기면 세 개의 반복문으로 결과 행렬의 각 성분을 계산할 수 있습니다.
코딩테스트: 행렬의 곱셈
이 문제는 2차원 배열을 순회하며 행과 열의 곱을 누적하는 과정을 코드로 옮기는 연습입니다.
문제
2차원 행렬 A A A 와 B B B 를 입력받아 행렬 A A A 에 행렬 B B B 를 곱한 결과를 반환하는 solution() 함수를 완성하세요.
제약조건
행렬 A A A , B B B 의 행과 열의 길이는 2 이상 100 이하입니다.
행렬 A A A , B B B 의 데이터는 -10 이상 20 이하인 정수입니다.
문제 풀이 (JavaScript)
function solution ( A , B ) {
const m = A . length ;
const n = A [ 0 ] . length ;
const p = B [ 0 ] . length ;
if ( n !== B . length ) throw new Error ( "행렬의 곱이 성립되지 않음" ) ;
const C = Array . from ( { length : m } , ( ) => new Array ( p ) . fill ( 0 ) ) ;
for ( let i = 0 ; i < m ; i ++ ) {
for ( let j = 0 ; j < p ; j ++ ) {
for ( let k = 0 ; k < n ; k ++ ) {
C [ i ] [ j ] += A [ i ] [ k ] * B [ k ] [ j ] ;
}
}
}
return C ;
}
코드 설명
m , n , p m, n, p m , n , p 는 각각 A A A 의 행 개수, A A A 의 열 개수(B B B 의 행 개수), B B B 의 열 개수를 나타냅니다.
n !== B.length 조건으로 행렬 곱셈이 가능한지 확인합니다.
결과 행렬 C C C 를 m × p m \times p m × p 크기의 0으로 채운 배열로 초기화합니다.
세 개의 for 문으로 행렬 곱셈을 수행합니다.
i는 A A A 의 행 인덱스입니다.
j는 B B B 의 열 인덱스입니다.
k는 A A A 의 열과 B B B 의 행을 연결하는 인덱스입니다.
C[i][j] += A[i][k] * B[k][j]로 성분의 곱을 누적합니다.
계산을 마친 C C C 를 반환합니다.