|
| 1 | +--- |
| 2 | +id: matrix-chain-multiplication |
| 3 | +title: Matrix Chain Multiplication using Dynamic Programming |
| 4 | +sidebar_label: Matrix Chain Multiplication |
| 5 | +tags: [python, java, c++, javascript, programming, algorithms, dynamic programming, tutorial, in-depth] |
| 6 | +description: In this tutorial, we will learn about the Matrix Chain Multiplication problem and its solution using Dynamic Programming in Python, Java, C++, and JavaScript with detailed explanations and examples. |
| 7 | +--- |
| 8 | + |
| 9 | +# Matrix Chain Multiplication using Dynamic Programming |
| 10 | + |
| 11 | +Matrix Chain Multiplication is a classic optimization problem that can be solved efficiently using dynamic programming. The goal is to determine the most efficient way to multiply a given sequence of matrices. |
| 12 | + |
| 13 | +## Problem Statement |
| 14 | + |
| 15 | +Given a sequence of matrices, find the optimal way to parenthesize the matrix product such that the total number of scalar multiplications is minimized. |
| 16 | + |
| 17 | +### Intuition |
| 18 | + |
| 19 | +Matrix multiplication is associative, meaning that the order of multiplication can affect the computation cost. Dynamic programming helps by breaking down the problem into subproblems, solving each subproblem just once, and storing the results. |
| 20 | + |
| 21 | +## Dynamic Programming Approach |
| 22 | + |
| 23 | +Using dynamic programming, we build a table `dp` where `dp[i][j]` represents the minimum number of scalar multiplications needed to compute the product of matrices from `i` to `j`. |
| 24 | + |
| 25 | +## Pseudocode for Matrix Chain Multiplication using DP |
| 26 | + |
| 27 | +#### Initialize: |
| 28 | + |
| 29 | +```markdown |
| 30 | +for i from 1 to n: |
| 31 | + dp[i][i] = 0 |
| 32 | + |
| 33 | +for len from 2 to n: |
| 34 | + for i from 1 to n-len+1: |
| 35 | + j = i+len-1 |
| 36 | + dp[i][j] = inf |
| 37 | + for k from i to j-1: |
| 38 | + q = dp[i][k] + dp[k+1][j] + p[i-1]*p[k]*p[j] |
| 39 | + if q < dp[i][j]: |
| 40 | + dp[i][j] = q |
| 41 | + |
| 42 | +return dp[1][n] |
| 43 | +``` |
| 44 | +### Example Output: |
| 45 | + |
| 46 | +Given the matrix dimensions: |
| 47 | +- `p = [1, 2, 3, 4]` |
| 48 | + |
| 49 | +The minimum number of multiplications required to multiply the sequence of matrices is `18`. |
| 50 | + |
| 51 | +The matrix dimensions correspond to three matrices: |
| 52 | +- A (1x2) |
| 53 | +- B (2x3) |
| 54 | +- C (3x4) |
| 55 | + |
| 56 | +By following the dynamic programming approach, the optimal way to multiply these matrices requires `18` scalar multiplications. The output is printed as: `Minimum number of multiplications is: 18` |
| 57 | + |
| 58 | +You can verify this by manually checking the optimal parenthesization: `((A * B) * C) requires (1*2*3) + (1*3*4) = 6 + 12 = 18` multiplications. |
| 59 | + |
| 60 | + |
| 61 | +## Implementing Matrix Chain Multiplication using DP |
| 62 | + |
| 63 | +### Python Implementation |
| 64 | + |
| 65 | +```python |
| 66 | +def matrix_chain_order(p): |
| 67 | + n = len(p) - 1 # Number of matrices |
| 68 | + dp = [[0 for _ in range(n)] for _ in range(n)] # DP table initialization |
| 69 | + |
| 70 | + # l is the chain length |
| 71 | + for l in range(2, n + 1): # l=2 means multiplying two matrices at a time |
| 72 | + for i in range(n - l + 1): |
| 73 | + j = i + l - 1 |
| 74 | + dp[i][j] = float('inf') # Initialize with infinity |
| 75 | + for k in range(i, j): |
| 76 | + # q = cost/scalar multiplications |
| 77 | + q = dp[i][k] + dp[k + 1][j] + p[i] * p[k + 1] * p[j + 1] |
| 78 | + if q < dp[i][j]: |
| 79 | + dp[i][j] = q |
| 80 | + |
| 81 | + return dp[0][n - 1] # Minimum cost to multiply all matrices |
| 82 | + |
| 83 | +p = [1, 2, 3, 4] |
| 84 | +print("Minimum number of multiplications is:", matrix_chain_order(p)) |
| 85 | +``` |
| 86 | + |
| 87 | +### Java Implementation |
| 88 | + |
| 89 | +```java |
| 90 | +public class MatrixChainMultiplication { |
| 91 | + public static int matrixChainOrder(int[] p) { |
| 92 | + int n = p.length - 1; // Number of matrices |
| 93 | + int[][] dp = new int[n][n]; // DP table initialization |
| 94 | + |
| 95 | + // l is the chain length |
| 96 | + for (int l = 2; l <= n; l++) { // l=2 means multiplying two matrices at a time |
| 97 | + for (int i = 0; i < n - l + 1; i++) { |
| 98 | + int j = i + l - 1; |
| 99 | + dp[i][j] = Integer.MAX_VALUE; // Initialize with infinity |
| 100 | + for (int k = i; k < j; k++) { |
| 101 | + // q = cost/scalar multiplications |
| 102 | + int q = dp[i][k] + dp[k + 1][j] + p[i] * p[k + 1] * p[j + 1]; |
| 103 | + if (q < dp[i][j]) { |
| 104 | + dp[i][j] = q; |
| 105 | + } |
| 106 | + } |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + return dp[0][n - 1]; // Minimum cost to multiply all matrices |
| 111 | + } |
| 112 | + |
| 113 | + public static void main(String[] args) { |
| 114 | + int[] p = {1, 2, 3, 4}; |
| 115 | + System.out.println("Minimum number of multiplications is: " + matrixChainOrder(p)); |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +``` |
| 120 | +### C++ Implementation |
| 121 | + |
| 122 | +```cpp |
| 123 | +#include <iostream> |
| 124 | +#include <climits> |
| 125 | +using namespace std; |
| 126 | + |
| 127 | +int matrixChainOrder(int p[], int n) { |
| 128 | + int dp[n][n]; // DP table initialization |
| 129 | + |
| 130 | + // l is the chain length |
| 131 | + for (int i = 1; i < n; i++) |
| 132 | + dp[i][i] = 0; // Cost is zero when multiplying one matrix |
| 133 | + |
| 134 | + for (int l = 2; l < n; l++) { // l=2 means multiplying two matrices at a time |
| 135 | + for (int i = 1; i < n - l + 1; i++) { |
| 136 | + int j = i + l - 1; |
| 137 | + dp[i][j] = INT_MAX; // Initialize with infinity |
| 138 | + for (int k = i; k <= j - 1; k++) { |
| 139 | + // q = cost/scalar multiplications |
| 140 | + int q = dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j]; |
| 141 | + if (q < dp[i][j]) { |
| 142 | + dp[i][j] = q; |
| 143 | + } |
| 144 | + } |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + return dp[1][n - 1]; // Minimum cost to multiply all matrices |
| 149 | +} |
| 150 | + |
| 151 | +int main() { |
| 152 | + int p[] = {1, 2, 3, 4}; |
| 153 | + int n = sizeof(p) / sizeof(p[0]); |
| 154 | + cout << "Minimum number of multiplications is: " << matrixChainOrder(p, n); |
| 155 | + return 0; |
| 156 | +} |
| 157 | +``` |
| 158 | +
|
| 159 | +### JavaScript Implementation |
| 160 | +
|
| 161 | +```javascript |
| 162 | +function matrixChainOrder(p) { |
| 163 | + let n = p.length - 1; // Number of matrices |
| 164 | + let dp = Array.from({ length: n }, () => Array(n).fill(0)); // DP table initialization |
| 165 | +
|
| 166 | + // l is the chain length |
| 167 | + for (let l = 2; l <= n; l++) { // l=2 means multiplying two matrices at a time |
| 168 | + for (let i = 0; i < n - l + 1; i++) { |
| 169 | + let j = i + l - 1; |
| 170 | + dp[i][j] = Infinity; // Initialize with infinity |
| 171 | + for (let k = i; k < j; k++) { |
| 172 | + // q = cost/scalar multiplications |
| 173 | + let q = dp[i][k] + dp[k + 1][j] + p[i] * p[k + 1] * p[j + 1]; |
| 174 | + if (q < dp[i][j]) { |
| 175 | + dp[i][j] = q; |
| 176 | + } |
| 177 | + } |
| 178 | + } |
| 179 | + } |
| 180 | +
|
| 181 | + return dp[0][n - 1]; // Minimum cost to multiply all matrices |
| 182 | +} |
| 183 | +
|
| 184 | +let p = [1, 2, 3, 4]; |
| 185 | +console.log("Minimum number of multiplications is:", matrixChainOrder(p)); |
| 186 | +``` |
| 187 | + |
| 188 | +## Complexity Analysis |
| 189 | + |
| 190 | +- Time Complexity: $O(n^3)$, due to the three nested loops. |
| 191 | +- Space Complexity: $O(n^2)$, for storing the DP table. |
| 192 | + |
| 193 | +## Conclusion |
| 194 | + |
| 195 | +Dynamic programming provides an efficient solution for the Matrix Chain Multiplication problem by breaking it into subproblems and storing intermediate results. This technique can be extended to solve other problems with overlapping subproblems and optimal substructure properties. |
0 commit comments