행렬 A1, A2, A3, . . . , An을 곱하는 수를 최소로 하는 최적의 곱셈 순서를 구하라.
예로, 행렬 A(2 × 3), B(3 × 5), C(5 × 2)가 있을 때 곱할 수 있는 경우의 수는 다음과 같다.
(AB)C = 2 × 3 × 5 + 2 × 5 × 2 = 50
A(BC) = 3 × 5 × 2 + 2 × 3 × 2 = 42
두 경우의 수에서 최솟값인 42에 해당하는 연산이 최소의 곱셈 횟수 42와 최적의 곱셈 순서 A(BC) 가 된다.
마지막 행렬 곱셈이 수행되는 상황을 다음과 같이 정리할 수 있으며, 경우의 수는 총 n - 1이다.
점화식
Ak의 크기 : pk-1 * pk
m[i, j] : 행렬 Ai, …, Aj의 곱을 계산하는 최소 비용
m[i, j] = {
0 ( if i = j )
min{ m[i, k] + m[k + 1, j] + pi-1 * pk * pj } ( if i < j ) ※ i <= k <= j-1
}
※ 밑줄 : 일반형 (Ai ×. . .× Ak)(Ak ×. . .× Aj)
행렬 곱셈 순서 : 재귀적 알고리즘 (효율적 측면에서 부적합)
minSum(i, j) { # 행렬곱 Ai...Aj를 구하는 최소 비용 구하기
if (i = j) then return 0; # 행렬이 하나뿐인 경우의 비용은 0
min ← -INF;
for k ← i to j-1 {
q ← minSum(i, k) + minSum(k + 1, j) + pi-1 * pk * pj;
if (q < min) then min ← q;
}
return min;
}
print(minSum(1, n))
행렬 곱셈 순서 : 다이나믹 프로그래밍
for i ← 1 to n
m[i, i] ← 0; # 행렬이 하나뿐인 경우의 비용은 0
for r ← 1 to n-1 { # r: 문제 크기를 결정하는 변수, r = j - i
for i ← 1 to n-r {
j ← i+r;
m[i, j] ← min{m[i, k] + m[k + 1, j] + pi-1 * pk * pj};
}
}
print (m[1, n]);
# Θ(n^3)