[알고리즘설계] 행렬 곱셈을 위한 스트라센 알고리즘
[알고리즘 설계] 행렬 곱셉을 위한 스트라센 알고리즘
1. 기본적인 행렬의 곱 연산
- 행렬 A와 B가 n * n의 정사각 행렬일 경우, 두 행렬의 곱 C = A * B를 정의하면, cij = Σ(k=1~n) aik * bkj (cij 는 행렬 C의 원소).
SQUARE-MATRIX-MULTIPLY(A,B)
n= A.rows
C는 새로운 n * n 행렬이라고 하자.
for i =1 to n
for j=1 to n
cij = 0
for k=1 to n
cij = cij + aik * bkj
return C
- 3중 루프의 각 루프는 반복을 정확히 n번 수행하고 cij = cij + aik * bkj 이 행은 상수시간이 걸리므로 이 프로시저는 Θ(n^3) 시간이 걸린다.
- 행렬 곱셈의 자연스러운 정의 자체가 많은 곱셈을 포함하고 있기 때문에 처음에는 어떤 행렬 곱셈 알고리즘도 하계가 Ω(n^3) 시간이 걸릴수밖에 없다고 생각할 수도 있지만, 이보다 짧은 알고리즘은 꽤 존재.
- 스트라센 알고리즘은 Θ(n^lg7) 의 수행시간을 가진다.
2. 단순한 분할정복 알고리즘
- 분할 정복 알고리즘을 통해 행렬의 곱 C= A * B 를 계산할때는 각 n * n 행렬의 n이 정확하게 2의 거듭제곱이 된다고 가정. 각 단계에서 n * n 행렬을 네개의 n/2 * n/2로 나눈다.
- A,B,C 행렬을 각각 네개의 n/2 * n/2 행렬로 나눈다고 하면, A=(A11 A12 A21 A22), B=(B11 B12 B21 B22), C=(C11 C12 C21 C22)로 나누어진다.
- C11 = A11 * B11 + A12 * B21
- C12 = A11 * B12 + A12 * B22
- C21 = A21 * B11 + A22 * B21
- C22 = A21 * B12 + A22 * B22
- 이 등식을 이용하여 재귀적인 분할정복 알고리즘을 만든다.
SQUARE-MATRIX-MULTIPLY-RECURSIVE(A,B)
n = A.rows
C는 n * n 행렬이라 하자
if n==1
c11 = a11 * b11
else
C11 = SQUARE-MATRIX-MULTIPLY-RECURSIVE(A11,B11) +
SQUARE-MATRIX-MULTIPLY-RECURSIVE(A12,B21)
C12 = SQUARE-MATRIX-MULTIPLY-RECURSIVE(A11,B12) +
SQUARE-MATRIX-MULTIPLY-RECURSIVE(A12,B22)
C21 = SQUARE-MATRIX-MULTIPLY-RECURSIVE(A21,B11) +
SQUARE-MATRIX-MULTIPLY-RECURSIVE(A22,B21)
C22 = SQUARE-MATRIX-MULTIPLY-RECURSIVE(A21,B12) +
SQUARE-MATRIX-MULTIPLY-RECURSIVE(A22,B22)
return C
- 이 경우 새로운 n/2 * n/2 행렬 12개를 만들 때 원 행렬의 행 인덱스 범위와 열 인덱스 범위를 이용하여 부분행렬을 만드므로 Θ(1)에 수행할 수 있다.
- n==1 인 경우는 단순히 하나의 스칼라 곱셈을 하면 되므로 T(1)=Θ(1)이 된다.
- n>1 인 경우는 행렬을 나누는 Θ(1) 과 각 재귀호출이 8번 일어나므로 8 * T(n/2)의 시간이 필요하다. 또한 각 부분행렬이 n^2/4 의 원소를 가지고 있으므로 4번의 행렬 덧셈은 각각 Θ(n^2) 시간이 걸린다.
- 즉 수행시간에 대한 점화식을 만들면 아래와 같이 표현된다.
T(n) = Θ(1) , if n=1
= 8T(n/2) + Θ(n^2) , if n>1
- 마스터 정리를 이용하면, T(n) = Θ(n^3)이 된다는 것을 알 수 있다. 이 경우는 일반적인 곱셈연산 수행시간과 다를것이 없다.
3. 스트라센 방법
-
스트라센이 사용한 방법의 핵심은 재귀 트리의 가지 수를 줄이는 것이다.
-
위의 분할정복에선 8번의 재귀호출이 일어나지만 스트라센 방법에선 7번의 재귀호출을 한다. 한번의 행렬 곱셈을 줄이는 대신 덧셈 횟수를 늘린다.
- 스트라센 방법의 네단계.
- 부분행렬로 나눈다.
- 10개의 행렬 S1,S2, … , S10을 만든다. 각 행렬의 크기는 n/2 * n/2
- 1단계에서 만든 부분행렬과 2단계에서 만든 10개의 행렬을 이용해 7개의 행렬 곱 P1,P2,…,P7을 계산한다. 각 행렬 Pi 는 n/2 * n/2 크기이다.
- Pi 행렬들의 다양한 조합을 더하거나 빼서 얻고자한 결과 행렬 C의 부분 행렬 C11,C12,C21,C22를 계산한다.
- 1,2,4단계에선 Θ(n^2) 시간이 걸리고, 3단계에선 재귀호출을 7번 한다.
T(n) = Θ(1) , if n=1
= 7T(n/2) + Θ(n^2) , if n>1
- T(n)은 마스터 정리에 의해 Θ(n^lg7)이 된다.
- S행렬에 관한 식.
- S1 = B12 - B22
- S2 = A11 + A12
- S3 = A21 + A22
- S4 = B21 - B11
- S5 = A11 + A22
- S6 = B11 + B22
- S7 = A12 - A22
- S8 = B21 + B22
- S9 = A11 - A21
- S10 = B11 + B12
- 위 단계에선 n/2 * n/2 행렬을 10번 더하거나 빼므로 Θ(n^2) 시간이 걸린다.
- 다음으론 n/2 * n/2 행렬을 7번 재귀적으로 곱한다.
- P1 = A11 * S1 =A11 * B12 - A11 * B22
- P2 = S2 * B22 = A11 * B22 + A12 * B22
- P3 = S3 * B11 = A21 * B11 + A22 * B11
- P4 = A22 * S4 = A22 * B21 - A22 * B11
- P5 = S5 * S6 = A11 * B11 + A11 * B22 + A22 * B11 + A22 * B22
- P6 = S7 * S8 = A12 * B21 + A12 * B22 - A22 * B21 - A22 * B22
- P7 = S9 * S10 = A11 * B11 + A11 * B12 - A21 * B11 - A21 * B12
- 마지막 단계에선 윗 단에서 만든 행렬 Pi를 이용하여 행렬 곱 C의 부분 행렬 4개를 만든다.
- C11 = P5 + P4 - P2 + P6
- C12 = P1 + P2
- C21 = P3 + P4
- C22 = P5 + P1 - P3 - P7
- 스트라센 알고리즘은 기본적인 행렬의 곱셈 프로시저보다 점근적으로 더 빠른 알고리즘이기 때문에 처음 발표되었을 때 굉장한 흥분을 불러 일으켰다.
- 현재까지 n * n 행렬을 곱하는 점근적으로 가장 빠르고 효율적인 알고리즘은 Coppersmith 와 Winograd 가 개발한 Ο(n^2.376) 이다.
- 행렬의 곱셈은 n^2의 원소를 채워야하기 때문에 하계는 명백하게 Ω(n^2)이다.
- 스트라센은 실용적인 관점에서 좋은 선택이 아닐 수 있는데, 희소행렬의 경우 희소행렬에 특화된 방법이 더빠르며, 정수가 아닌 값에 대해서는 오차가 일반적인 방법보다 많이 쌓인다. 또한 재귀호출단계에서 생기는 부분행렬이 메모리를 차지하게 된다.
댓글남기기