Skip to main content

Command Palette

Search for a command to run...

Matrix Chain Multiplication (MCM)

Published
5 min readView as Markdown
C

I share my learnings here. Thanks for reading.

Problem

Given an array arr[] which represents the dimensions of a sequence of matrices where the ith matrix has the dimensions (arr[i-1] x arr[i]) for i>=1, find the most efficient way to multiply these matrices together. The efficient way is the one that involves the least number of multiplications. (link).

Find the minimum number of operations needed to multiply all the matrices.

Examples:

Input: arr[] = [2, 1, 3, 4]
Output: 20
Explanation: There are 3 matrices of dimensions 2 × 1, 1 × 3, and 3 × 4, Let this 3 input matrices be M1, M2, and M3. There are two ways to multiply: ((M1 x M2) x M3) and (M1 x (M2 x M3)), note that the result of (M1 x M2) is a 2 x 3 matrix and result of (M2 x M3) is a 1 x 4 matrix. 
((M1 x M2) x M3)  requires (2 x 1 x 3) + (2 x 3 x 4) = 30 
(M1 x (M2 x M3))  requires (1 x 3 x 4) + (2 x 1 x 4) = 20. 
The minimum of these two is 20.
Input: arr[] = [1, 2, 3, 4, 3]
Output: 30
Explanation: There are 4 matrices of dimensions 1 × 2, 2 × 3, 3 × 4, 4 × 3. Let this 4 input matrices be M1, M2, M3 and M4. The minimum number of multiplications are obtained by ((M1 x M2) x M3) x M4). The minimum number is (1 x 2 x 3) + (1 x 3 x 4) + (1 x 4 x 3) = 30.
Input: arr[] = [3, 4]
Output: 0
Explanation: As there is only one matrix so, there is no cost of multiplication.

Constraints:
2 ≤ arr.size() ≤ 100
1 ≤ arr[i] ≤ 200

Partition DP Pattern

Introduction

When there are multiple ways to solve a problem, we need to use the partition DP approach.

1 + 2 + 3 × 5

(1+2+3) x 5

(1+2) + (3 x5)

Partitioning can occur anywhere, so we should calculate all possible ways to get the minimum or maximum result.

MCM Basics

A (2x2) 
1 2
3 1

B (2x1)
2
3

A x B
=
1x2+2x3
3x2+1x3

=
8
9

Total mulitplications = 2 x 2 x 1 = 4
ABC 

A = 10 × 30

B = 30 × 5

C = 5 × 60

(AB)xC
Number of operations for AB (10,5) multiplication = 10 × 30 × 5 = 1500
Number of operations for (AB)xC (10,60) multiplication = 10 × 5 × 60 = 3000
total operations = 4500

Ax(BC)
Number of operations for BC (30,60) multiplication = 30 × 5 × 60 = 9000
Number of operations for Ax(BC) (10,60) multiplication = 10 × 30 × 60 = 18000
total operations = 27000

Minimum number of operations is 4500

Matrices dimensions

Given an arr[] = [10, 20, 30, 40, 50]

This resembles the dimensions of N-1 matrices. So total N-1 matrices.

A = 10 × 20

B = 20×30

C = 30×40

D = 40×50

Dimension of ith matrix = arr[i-1] x arr[i]

Partitioning DP Rules

Rule 1 : Start with entire block f(i, j) where i is the start point and j is the end point.

ABCD

(A) (BCD)
(AB) (CD)
(ABC) (D)

Rule 2: Try all partitions (Run a loop to try all partitions)

Rule 3: Return the best possible 2 partiions.

Example - ABCD

Fixed operations

We can easily calculate the operations of the 2 partitions without computing the each partitions operations because we know the resultant matrix dimensions

A (BCD)

Dimensions
A = 10 × 20

B = 20×30

C = 30×40

D = 40×50

BCD dimensions = 20 x 50

A BCD operations = 10 x 20 x 50

if we assume partition is at the kth index
Then our answer would be arr[i-1] x arr[k] x arr[j]

Solution

Recursion

Time - O(expotential)

Space - O(n)

class Solution {

    static int f(int i, int j, int[] arr){

        if(i==j) return 0;

        int ans = Integer.MAX_VALUE;

        for(int k=i; k<j; k++){

            /**
             * AK K+1B
             * (i-1, i) .... (k-1, k) - (k, k+1) ... (j-1, j)
             * (i-1, k) - (k, j)
             * Fixed operations i-1 x k x j
            */

            int fixedOperations = arr[i-1] * arr[k] * arr[j];
            int totalOperations = fixedOperations + f(i, k, arr) + f(k+1, j, arr);

            ans = Math.min(ans, totalOperations);
        }

        return ans;
    }

    static int matrixMultiplication(int arr[]) {


        return f(1, arr.length-1, arr);
    }
}

Memoization

Time - O(nxn) x O(n) → loop = O(n³)

Space - O(nxn) + O(n)

class Solution {

    static int f(int i, int j, int[] arr, int[][] dp){

        if(i==j) return 0;

        if(dp[i][j]!=-1) return dp[i][j];

        int ans = Integer.MAX_VALUE;

        for(int k=i; k<j; k++){

            /**
             * AK K+1B
             * (i-1, i) x (k-1, k) - (k, k+1) x (j-1, j)
             * (i-1, k) - (k, j)
             * Fixed operations i-1 x k x j
            */

            int fixedOperations = arr[i-1] * arr[k] * arr[j];
            int totalOperations = fixedOperations + f(i, k, arr, dp) + f(k+1, j, arr, dp);

            ans = Math.min(ans, totalOperations);
        }

        return dp[i][j]=ans;
    }

    static int matrixMultiplication(int arr[]) {
        int n = arr.length;
        int[][] dp = new int[n][n];

        for(int[] row : dp){
            Arrays.fill(row, -1);
        }

        return f(1, n-1, arr, dp);
    }
}

Tabulation

Base case i==j we need to put 0 in the dp but while initializationg itself the dp is initialized with all 0’s.

Time - O(n³)

Space - O(nxn)

class Solution {


    static int matrixMultiplication(int arr[]) {
        int n = arr.length;
        int[][] dp = new int[n][n];


        for(int i=n-1; i>=1; i--){
            for(int j=i+1; j<n; j++){
                int ans = Integer.MAX_VALUE;

                for(int k=i; k<j; k++){

                    /**
                     * AK K+1B
                     * (i-1, i) x (k-1, k) - (k, k+1) x (j-1, j)
                     * (i-1, k) - (k, j)
                     * Fixed operations i-1 x k x j
                    */

                    int fixedOperations = arr[i-1] * arr[k] * arr[j];
                    int totalOperations = fixedOperations 
                                        + dp[i][k] 
                                        + dp[k+1][j];

                    ans = Math.min(ans, totalOperations);
                }

                dp[i][j] = ans;
            }
        }

        return dp[1][n-1];
    }
}