#!/usr/bin/env python # coding: utf-8 # This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges). # # Solution Notebook # ## Problem: Given a list of 2x2 matrices, minimize the cost of matrix multiplication. # # * [Constraints](#Constraints) # * [Test Cases](#Test-Cases) # * [Algorithm](#Algorithm) # * [Code](#Code) # * [Unit Test](#Unit-Test) # ## Constraints # # * Do we just want to calculate the cost and not list the actual order of operations? # * Yes # * Can we assume the inputs are valid? # * No # * Can we assume this fits memory? # * Yes # ## Test Cases # # * None -> Exception # * [] -> 0 # * [Matrix(2, 3), Matrix(3, 6), Matrix(6, 4), Matrix(4, 5)] -> 124 # ## Algorithm # # We'll use bottom up dynamic programming to build a table. # #
# 
#   0    1    2    3
# [2,3][3,6][6,4][4,5]
# 
# Case: 0 * 1
# 2 * 3 * 6 = 36
# 
# Case: 1 * 2
# 3 * 6 * 4 = 72
# 
# Case: 2 * 3
# 6 * 4 * 5 = 120
# 
# Case: 0 * 1 * 2
# 0 * (1 * 2) = 2 * 3 * 4 + 72 = 96
# (0 * 1) * 2 = 36 + 2 * 6 * 4 = 84
# min: 84
# 
# Case: 1 * 2 * 3
# 1 * (2 * 3) = 3 * 6 * 5 + 120 = 210
# (1 * 2) * 3 = 72 + 3 * 4 * 5 = 132
# min: 132
# 
# Case: 0 * 1 * 2 * 3
# 0 * (1 * 2 * 3) = 2 * 3 * 5 + 132 = 162
# (0 * 1) * (2 * 3) = 36 + 120 + 2 * 6 * 5 = 216
# (0 * 1 * 2) * 3 = 84 + 2 * 4 * 5 = 124
# min: 124
# 
#   ---------------------
#   | 0 |  1 |  2 |   3 |
#   ---------------------
# 0 | 0 | 36 | 84 | 124 |
# 1 | x |  0 | 72 | 132 |
# 2 | x |  x |  0 | 120 |
# 3 | x |  x |  x |   0 |
#   ---------------------
# 
# min cost = T[0][cols-1] = 124
# 
# for k in range(i, j):
#     T[i][j] = minimum of (T[i][k] + T[k+1][j] +
#                           m[i].first * m[k].second * m[j].second) for all k
# 
# ### Explanation of k #
#   0    1    2    3
# [2,3][3,6][6,4][4,5]
# 
# Fill in the missing cell, where i = 0, j = 3
# 
#   ---------------------
#   | 0 |  1 |  2 |   3 |
#   ---------------------
# 0 | 0 | 36 | 84 | ??? |
# 1 | x |  0 | 72 | 132 |
# 2 | x |  x |  0 | 120 |
# 3 | x |  x |  x |   0 |
#   ---------------------
# 
# Case: 0 * (1 * 2 * 3), k = 0
# i = 0, j = 3
# 
# 0 * (1 * 2 * 3) = 2 * 3 * 5 + 132 = 162
# T[i][k] + T[k+1][j] + m[i].first * m[k].second * m[j].second
# T[0][0] + T[1][3] + 2 * 3 * 5
# 0 + 132 + 30 = 162
# 
# Case: (0 * 1) * (2 * 3), k = 1
# i = 0, j = 3
# 
# (0 * 1) * (2 * 3) = 36 + 120 + 2 * 6 * 5 = 216
# T[i][k] + T[k+1][j] + m[i].first * m[k].second * m[j].second
# T[0][1] + T[2][3] + 2 * 6 * 5
# 36 + 120 + 60 = 216
# 
# Case: (0 * 1 * 2) * 3, k = 2
# i = 0, j = 3
# 
# (0 * 1 * 2) * 3 = 84 + 2 * 4 * 5 = 124
# T[i][k] + T[k+1][j] + m[i].first * m[k].second * m[j].second
# T[0][2] + T[3][3] + 2 * 4 * 5
# 84 + 0 + 40 = 124
# 
# 
# # Complexity: # * Time: O(n^3) # * Space: O(n^2) # ## Code # In[1]: class Matrix(object): def __init__(self, first, second): self.first = first self.second = second # In[2]: import sys class MatrixMultiplicationCost(object): def find_min_cost(self, matrices): if matrices is None: raise TypeError('matrices cannot be None') if not matrices: return 0 size = len(matrices) T = [[0] * size for _ in range(size)] for offset in range(1, size): for i in range(size-offset): j = i + offset min_cost = sys.maxsize for k in range(i, j): cost = (T[i][k] + T[k+1][j] + matrices[i].first * matrices[k].second * matrices[j].second) if cost < min_cost: min_cost = cost T[i][j] = min_cost return T[0][size-1] # ## Unit Test # In[3]: get_ipython().run_cell_magic('writefile', 'test_find_min_cost.py', "import unittest\n\n\nclass TestMatrixMultiplicationCost(unittest.TestCase):\n\n def test_find_min_cost(self):\n matrix_mult_cost = MatrixMultiplicationCost()\n self.assertRaises(TypeError, matrix_mult_cost.find_min_cost, None)\n self.assertEqual(matrix_mult_cost.find_min_cost([]), 0)\n matrices = [Matrix(2, 3),\n Matrix(3, 6),\n Matrix(6, 4),\n Matrix(4, 5)]\n expected_cost = 124\n self.assertEqual(matrix_mult_cost.find_min_cost(matrices), expected_cost)\n print('Success: test_find_min_cost')\n\n\ndef main():\n test = TestMatrixMultiplicationCost()\n test.test_find_min_cost()\n\n\nif __name__ == '__main__':\n main()\n") # In[4]: get_ipython().run_line_magic('run', '-i test_find_min_cost.py')