matrix chain

행렬의 곱셈

행렬의 곱셈

  • 행렬의 곱셈은 아래와 같이 이루어진다.

    • 행렬 A (p, q) * 행렬 B (q, r) = 행렬 C(p, r)

    • 즉, A행렬의 열의 길이와 B행렬의 행의 길이가 같아야만 행렬 간 곱셈이 성립될 수 있고, 계산한 결과 행렬 C는 행의 길이가 p이고, 열의 길이가 r인 행렬이 된다.

    • 이때, 연산의 횟수는 p*q*r 이 된다.

  • 행렬의 곱셈에서는 교환법칙이 성립하지 않는다. 행렬 A와 B 간에 곱셈을 하기 위해서는 각각의 열과 행의 길이가 위에서 말한 것처럼 맞아야 하므로, AB = BA 를 반드시 보장할 수 없고, 어쩌면 교환법칙을 할 경우, 행렬의 곱셈 자체가 불가능해질수도 있다.

  • 하지만 결합법칙은 성립할 수 있다. 행렬이 3개 이상 곱해진다고 생각해보면, (AB)C = A(BC) 는 충분히 성립할 수 있다. 각각의 열과 행의 길이가 맞다는 것이 보장되기 때문이다.

Matrix-Chain, 세개 이상 행렬의 곱셈

  • 이렇게 세 개 이상의 행렬이 곱셈을 할 때, 결합법칙에 따라 연산 순서를 바꿀 수 있는데, 이 경우, 각각의 케이스마다 연산 횟수가 눈에 띄게 달라질 수 있다.

  • 예를 들어, 아래와 같다고 가정해보면,

    • 행렬 A 10 * 100

    • 행렬 B 100 * 5

    • 행렬 C 5 * 50

  • (AB)C = 10*100*5 + 10*5*50 = 7,500 번의 연산

  • A(BC) = 100*5*50 + 10*100*50 = 75,000 번의 연산

  • 연산 순서만 다르게 했을 뿐인데, 연산의 횟수가 10배나 차이가 나게 된다.

  • 따라서 matrix-chain 유형의 문제에서는 여러개의 행렬 곱셈에 대하여 어떤 연산 순서로 진행을 해야 가장 적거나 많은 연산을 하게 되는지 구하는 문제들이 출제가 된다.

    • n개의 행렬의 곱 A1A2A3 ... An을 계산하는 최적의 순서는? (이때, Ai는 Pk-1 * Pk)

optimal substructure

  • 행렬의 곱셈 결과는 결국 행렬의 형태를 띈다.

  • 행렬 z를 구하려면, 결국 행렬 x와 y의 곱을 해야한다. 그리고 행렬에서는 교환법칙이 성립하지 않기 때문에 이 x와 y는 1부터 k까지의 곱, k+1부터 n까지의 곱의 결과가 된다.

  • 결국 두 행렬의 곱에 대해서는 위와 같은 순환식을 만들 수 있을 것이다.

  • Ai부터 Aj까지의 행렬을 곱하는데 최소로 곱셈 연산을 하는 횟수를 구하려면,

    • i부터 임의의 지점 k까지의 최소 연산 횟수와

    • k+1부터 j까지의 최소 연산횟수를 더하고,

    • 두 결과의 연산 횟수인 pi-1 * pk*pj 를 곱한 값을 더해야한다.

      • 설명) Ai = pi-1 * pk, Aj = pk * pj 이므로 위와 같이 된다.

  • 이때, base case는 아래와 같은 논리로 구한다.

    • 결국, 행렬의 곱셈식 중에서 중간에 임의의 k값을 정하고 앞 뒤로 연산한 결과값을 더하는 연산이 반복된다는 것을 발견

    • 연산을 거듭할수록 i와 j는 서로 같아지게 될 것이다. 이것이 base case 가 된다.

    • i = j인 경우, 행렬 1개에 대해서 곱셈 연산의 최소 횟수는 아예 하지 않는 경우이므로, 0이 된다.

  • 그러면 이제 순환식을 계산하는 순서를 정해야한다. 이전 연산의 해가 다음 연산의 재료로 이용되기 위해서는 어떤 순서로 계산을 해야할까?

  • 순환식을 분석해보면 되는데,

    • 우선은 i <= j 이므로, m[i, k] 는 m[i, i], m[i, i+1], m[i, i+2], ... m[i, j-1] 까지의 값을 알아야 한다.

    • m[k+1, j] 의 경우는 m[i+1, j], m[i+2, j], m[i+3, j], ..., m[j, j] 까지의 값을 알아야 한다.

  • 그렇다면 이 경우에는 기존처럼 행 우선 연산을 이용할 수는 없고, 두 가지의 순서를 생각해볼 수 있다.

    • 하나는 밑에서 위로 왼쪽부터 오른쪽으로 계산하는 경우와

    • 또 다른 하나는 대각선으로부터 시작하여 점차 우상향하는 방법이 있다.

    • 사실 코딩하기에는 밑에서 위로 계산하는 방법이 쉬운데, 대부분의 예제에서는 대각선으로 구현한 것이 많이 등장한다.

  • 코드로 구현해보자면 위와 같다. 먼저 대각선을 모두 0으로 채운다.

  • 그다음 대각선을 우상향하는 방향으로 1부터 n-1 까지 순회한다. 대각선의 개수가 (i, i) 대각선을 제외하고 n-1개라는 것을 기억한다.

  • i 좌표는 1부터 n-r까지 순회하는데, 각 대각선의 값의 개수만큼을 의미한다. 대각선부터 시작해서 점차 1개씩 줄어드는데, 대각선에서 값의 개수가 n개이므로, 총 n-r개만큼의 값을 점검하게 된다.

  • 이때, j의 좌표값은 i 값에서 r만큼 이동한 위치가 되므로, i+r 이 된다.

  • (i, j)위치까지의 최소값 m[i, j]는 m[i, k] + m[k+1][j] 가 된다. 임의의 지점 k에 대하여 범위는 i<=k<=j-1 이 성립하므로, 우선 k=i 라고 시작점을 둔 후, k=i+1 부터 k=j-1까지 차례대로 순회하며 최소값을 비교한다.

    • 즉 가장 최소값이 되는 임의의 지점 k를 구하기 위해서 i부터 j-1까지 모든 경우의 수를 검사하는 것이다.

Last updated