이 글은 유튜브 주니온TV의 알고리즘 기초 수업을 바탕으로 작성된 글입니다. 이미지, 내용 등 모든 저작권은 채널을 운영하시는 교수님에게 있습니다.
Chained Matrix Mulipication
n개의 행렬을 곱하는 최적의 순서를 구하는 문제
2 X 3 3 X 4 행렬하면 2 X 4 행렬이 나오고
총 필요한 계산수는 2 X 3 X 4이다.
이와 관련하여 카탈란 수라는 개념이 있다.
이와 관련된 증명이 있지만 일단은 넘기도록하겠다.
이렇게 Brute-Force를 사용할 경우 만약 4개의 행렬을 두고 계산한다면, 총 5가지의 경우의 수가 존재한다.
보다시피 3번째 방법이 가장 빠른 계산법이라는걸 알 수 있고, 같은 행렬 계산이지만 계산수행횟수가 거의 10배정도 차이나는것을 알 수 있다.
이게 바로 연쇄행렬곱셈이 필요한 이유라 생각된다.
우선 더 진행하기 전에 행렬의 곱셈 조건에 대해서 간단히 짚고 넘어가도록 하자. 교수님꼐서 친절하게 정리해주셨다.
정리하자면 곱하려는 두 행렬 A, B중 A 행렬의 행 또는 열이, B 행렬의 열 또는 행과 같아야 한다는 뜻이다.
1단계 재귀관계식을 찾는다
2단계
재귀관계식은 사진으로 설명을 대체하도록 하겠다.
예를 들어 M[1][4]를 찾는다고하면 𝑚𝑖𝑛
( 𝑀[1][1] + 𝑀[2][4] + 𝑑0𝑑1𝑑4, // k가 1일때
𝑀[1][2] + 𝑀[3][4] + 𝑑0𝑑2𝑑4, // k가 2일때
𝑀[1][3] + 𝑀[4][4] + 𝑑0𝑑3𝑑4 ) // k가 3일때
의 과정을 통해서 가장 작은 diagoanl 의 합이 그 값이 되고 마지막 diagonal의 값이 optimal한 값이 된다.
def minmult(d) :
n = len(d) - 1
M = [[-1] * (n+1) for _ in range(n+1)]
P = [[-1] * (n + 1) for _ in range(n + 1)]
for i in range(1, n+1):
M[i][i] = 1
for diagonal in range(1, n):
for i in range(1, n - diagonal + 1):
j = i + diagonal
M[i][j] , P[i][j] = minimum(M, d, i ,j)
return M, P
재귀식을 실행하는 코드이다.
우선 M과 P 두 이중배열을 -1로 모두 초기화 한 다음, 중심선은 전부 1로 처리한다. 그 후 1 에서 n-1 까지 loop하고 그 대각선 안에서는 1에서 n - diagonal 까지 반복한다.
- 대각선이 4 3 2 1 이런식으로 줄어들기에 diagonal 만큼 빼준 수 까지 돌면 대각선의 모든 경우의 수를 계산하게 된다
그 후 j = i 값과 diagonal을 합한 값으로 설정하고
- 현재 diagnoal에서 몇번째의 경우인가를 찾는것
한가지 유의할 것은 1번 대각선의 경우 이미 초기값이 설정되어 있기에 2번대각선부터 계산을 수행하게 된다.
그때의 M[i][j]와 P[i][j]를 minimum 함수를 통해 결정한다.
def minimum(M ,d , i ,j):
INF = 999
minV = INF
minK = 0
for k in range(i, j):
value = M[i][k] + M[k+1][j]
value += d[i-1] * d[k] * d[j]
if (minV > value):
minV = value
minK = k
return minV, minK
최소값은 무한대, 위치는 0으로 두고 value의 값을 점화식에 따라 계산한다.
value = M[i][k] + M[k+1][j]
value += d[i-1] d[k] d[j]
그리고 계산을 마무리하계 된다.
M 은 Optimal Value를 가지고 있다. 우리가 알고 싶은 것은 어떤것이 최적의 순서인가이다.
따라서 P를 이용하여 이를 찾아낸다.
앞서 두 코드에서 P[i][j]에 K를 저장하였다. 이 뜻은 즉, (Ai ... Ak)(Ak+1...Aj)로 분활되어 계산된다는 뜻이고 이를 통해 우리는 어떠한 순서로 계산되었는지 파악 할 수 있다.
코드는 다음과 같다
def order(P, i, j):
if (i == j):
print('A%d'%(i), end = '')
else:
k = P[i][j]
print('(', end = '')
order(P,i,k)
order(P, k+1, j)
print(')', end = '')
마지막으로
INF = 999
d = [5, 2, 3, 4, 6, 7, 8]
M, P = minmult(d)
print('M =')
for i in range(1, len(M)):
print(M[i][1:])
print('P =')
for i in range(1, len(P)):
print(P[i][1:])
print('minimum order: ', end ='')
order(P, 1, len(d) - 1)
코드를 통해 실행시켜주면
다음과 같은 결과물이 나오게된다.
학교 정규 수업시간에는 배우지 않았던 알고리즘이라 이해하는데 어려움이 있었고 다 배우고 나서도 내가 과연 이걸 안보고 이해하고 증명하고 코드를 작성할 수 있을까? 라는 의문에 빠지게 되었다.
하지만 누가 그랬던가, 알고리즘은 모르겠으면 그냥 외우라고
이렇게 좋은 내용의 강의를 유튜브에서 무료로 재공해주시는 교수님에게 다시 한 번 감사의 인사를 올린다