Suppose we are given a sequence of matrix dimensions. We want to compute the product of the matrices. We are not given the matrices, just the dimensions. In which order should we multiply the matrices in order to minimize the number of operations?

Say we have:

We want to compute the product . Note that we can do this because the dimension of the rows of A is the dimension of the columns of B, etc. Matrix multiplication is associative, not commutative. , but .

Given two matrices of dimensions and , we may ballpark the cost of multiplying these two matrices as fixed point operations. From the problem example, consider the following parantheticalizations:

This discrepancy allows us to optimize. The number of possible associations grows exponentially, making search non-trivial. The number of parantheticalizations follows the Catalan numbers, which grow . The greedy approach won’t consistently yield the best answer, so we’ll need dynamic programming for this one.

Algorithm

We can trivially create trees for these associations where the children are the left and right terms, and the parent is the expression. We cannot, however, choose one binary tree from an algorithm, we need to consider all binary trees. The dynamic programming approach to this problem relies on the fact that each binary tree is a sub-tree of the root expression, meaning that we can store these trees in a dp structure and compute as solution by combining its minimal subproblems. We’ll define the db table as where the cost to multiply . Our base case comes from . At each level, we’re going to have some partition of the overall series. We’re going to find some midpoint to multiply two sub-matrices at, . The cost for the overall level is the cost for the two smaller two-multiplications and , plus the cost for the current multiplication. The very last step involves splitting the sequence of the matrices into two products to be computed and then combined. We’re looking at the minimum for this problem, so we need to take the minimum over all costs, which we do with this table: The for which this is the minimum corresponds to the costs to compute , plus the cost to multiply them together.

Pseudocode

We’re going to end up ignoring half the table because the recurrence only fills up the top half ().

def chainmatrix(d_0, d_1, ..., d_n):
	dp = [0; n][0; n]
	for i in 1..n:
		dp[i, i] = 0
 
	for s in 1..n:
		for i in 1..n-s:
			j = i+s
			dp[i, j] = min_ { i <= k < j } { dp [i , k ] + dp [ k + 1 , j ] + d_ { i - 1} d_kd_j }

In this algorithm, we need to fill in cells, each taking time. This leads to runtime.