The Matrix Chain Multiplication problem is a classic example of a dynamic programming problem. The problem is to determine the optimal order for multiplying a chain of matrices such that the total number of multiplications is minimized.
Suppose we have n matrices, and the dimensions of the i-th matrix are represented by a pair of numbers (a_i, b_i), such that matrix i has dimensions a_i b_i. For simplicity, we can assume that all matrices are compatible for multiplication, that is, b_i-1 = a_i for i = 2, 3, ..., n.
The problem is to find the optimal order to multiply the matrices such that the total number of scalar multiplications is minimized. The number of scalar multiplications required to multiply two matrices of dimensions a b and b c is a b c.
We can use dynamic programming to solve this problem by breaking it down into subproblems and computing the solution to each subproblem only once.
Let’s define the following variables:
- m[i,j], the minimum number of scalar multiplications needed to compute the product of matrices A[i]A[i+1]...A[j]
- s[i,j], the index of the matrix that should be used as the last in the multiplication of matrices A[i]A[i+1]...A[j]
The base case for our subproblem is when i == j, which means we have only one matrix, so m[i,i] = 0. For the general case, we can express m[i,j] recursively in terms of the subproblems as follows:
m[i,j] = min(m[i,k] + m[k+1,j] + A[i].row * A[k].col * A[j].col) for i k < j
Where A[i].row and A[k].col represent respectively the number of rows of matrix Ai and the number of columns of matrix Ak. A[k].col represents the number of columns of matrix Aj.
The variable s[i,j] represents the index of the matrix that should be used as the last in the multiplication of matrices A[i]A[i+1]...A[j]. We can compute this variable as follows:
s[i,j] = argmin(m[i,k] + m[k+1,j] + A[i].row * A[k].col * A[j].col) for i k < j
Once we have computed m and s for every subproblem, we can then use the s table to construct the optimal order for multiplying the matrices.
Here’s a Java implementation of the algorithm:
public static int[] matrixChainOrder(int[] p) {
int n = p.length - 1; // number of matrices
int[][] m = new int[n][n]; // minimum scalar multiplications
int[][] s = new int[n][n]; // matrix indexes
// base case: single matrix
for (int i = 0; i < n; i++) {
m[i][i] = 0;
}
// compute minimum scalar multiplications
for (int len = 2; len <= n; len++) {
for (int i = 0; i < n - len + 1; i++) {
int j = i + len - 1;
m[i][j] = Integer.MAX_VALUE;
for (int k = i; k < j; k++) {
int q = m[i][k] + m[k+1][j] + p[i]*p[k+1]*p[j+1];
if (q < m[i][j]) {
m[i][j] = q;
s[i][j] = k;
}
}
}
}
// construct optimal order
int[] order = new int[n];
int index = 0;
int i = 0, j = n - 1;
while (i < j) {
order[index++] = s[i][j];
if (s[i][j] >= i+1) {
order[index++] = s[i][j]-1;
}
i = s[i][j] + 1;
}
order[index] = n-2;
return order;
}
The function ‘matrixChainOrder‘ takes an array ‘p‘ that represents the dimensions of the matrices, and returns an array ‘order‘ that represents the optimal order for multiplying the matrices.
For example, if we have 4 matrices with dimensions 10x20, 20x30, 30x40, and 40x30, we can call ‘matrixChainOrder‘ as follows:
int[] p = {10, 20, 30, 40, 30};
int[] order = matrixChainOrder(p);
System.out.println(Arrays.toString(order));
This would output the following:
[1, 2, 3, 0, 2]
This means that the optimal order for multiplying the matrices is A1 * A2 * A3 * A0 * A2, where Ai represents the matrix with dimensions ‘p[i] x p[i+1]‘.