mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-27 15:01:08 +00:00
Dynamic programming/matrix chain multiplication (#10562)
* updating DIRECTORY.md * spell changes * updating DIRECTORY.md * real world applications * updating DIRECTORY.md * Update matrix_chain_multiplication.py Add a non-dp solution with benchmarks. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update matrix_chain_multiplication.py * Update matrix_chain_multiplication.py * Update matrix_chain_multiplication.py --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Pooja Sharma <poojasharma@MyBigMac.local> Co-authored-by: Christian Clauss <cclauss@me.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
d00888de76
commit
e6aae1cf66
|
@ -182,6 +182,7 @@
|
||||||
* [Permutations](data_structures/arrays/permutations.py)
|
* [Permutations](data_structures/arrays/permutations.py)
|
||||||
* [Prefix Sum](data_structures/arrays/prefix_sum.py)
|
* [Prefix Sum](data_structures/arrays/prefix_sum.py)
|
||||||
* [Product Sum](data_structures/arrays/product_sum.py)
|
* [Product Sum](data_structures/arrays/product_sum.py)
|
||||||
|
* [Sparse Table](data_structures/arrays/sparse_table.py)
|
||||||
* Binary Tree
|
* Binary Tree
|
||||||
* [Avl Tree](data_structures/binary_tree/avl_tree.py)
|
* [Avl Tree](data_structures/binary_tree/avl_tree.py)
|
||||||
* [Basic Binary Tree](data_structures/binary_tree/basic_binary_tree.py)
|
* [Basic Binary Tree](data_structures/binary_tree/basic_binary_tree.py)
|
||||||
|
@ -340,6 +341,7 @@
|
||||||
* [Longest Increasing Subsequence O(Nlogn)](dynamic_programming/longest_increasing_subsequence_o(nlogn).py)
|
* [Longest Increasing Subsequence O(Nlogn)](dynamic_programming/longest_increasing_subsequence_o(nlogn).py)
|
||||||
* [Longest Palindromic Subsequence](dynamic_programming/longest_palindromic_subsequence.py)
|
* [Longest Palindromic Subsequence](dynamic_programming/longest_palindromic_subsequence.py)
|
||||||
* [Longest Sub Array](dynamic_programming/longest_sub_array.py)
|
* [Longest Sub Array](dynamic_programming/longest_sub_array.py)
|
||||||
|
* [Matrix Chain Multiplication](dynamic_programming/matrix_chain_multiplication.py)
|
||||||
* [Matrix Chain Order](dynamic_programming/matrix_chain_order.py)
|
* [Matrix Chain Order](dynamic_programming/matrix_chain_order.py)
|
||||||
* [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py)
|
* [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py)
|
||||||
* [Max Product Subarray](dynamic_programming/max_product_subarray.py)
|
* [Max Product Subarray](dynamic_programming/max_product_subarray.py)
|
||||||
|
@ -370,6 +372,7 @@
|
||||||
* [Builtin Voltage](electronics/builtin_voltage.py)
|
* [Builtin Voltage](electronics/builtin_voltage.py)
|
||||||
* [Carrier Concentration](electronics/carrier_concentration.py)
|
* [Carrier Concentration](electronics/carrier_concentration.py)
|
||||||
* [Charging Capacitor](electronics/charging_capacitor.py)
|
* [Charging Capacitor](electronics/charging_capacitor.py)
|
||||||
|
* [Charging Inductor](electronics/charging_inductor.py)
|
||||||
* [Circular Convolution](electronics/circular_convolution.py)
|
* [Circular Convolution](electronics/circular_convolution.py)
|
||||||
* [Coulombs Law](electronics/coulombs_law.py)
|
* [Coulombs Law](electronics/coulombs_law.py)
|
||||||
* [Electric Conductivity](electronics/electric_conductivity.py)
|
* [Electric Conductivity](electronics/electric_conductivity.py)
|
||||||
|
@ -524,6 +527,7 @@
|
||||||
* [Simplex](linear_programming/simplex.py)
|
* [Simplex](linear_programming/simplex.py)
|
||||||
|
|
||||||
## Machine Learning
|
## Machine Learning
|
||||||
|
* [Apriori Algorithm](machine_learning/apriori_algorithm.py)
|
||||||
* [Astar](machine_learning/astar.py)
|
* [Astar](machine_learning/astar.py)
|
||||||
* [Data Transformations](machine_learning/data_transformations.py)
|
* [Data Transformations](machine_learning/data_transformations.py)
|
||||||
* [Decision Tree](machine_learning/decision_tree.py)
|
* [Decision Tree](machine_learning/decision_tree.py)
|
||||||
|
@ -554,7 +558,6 @@
|
||||||
* [Word Frequency Functions](machine_learning/word_frequency_functions.py)
|
* [Word Frequency Functions](machine_learning/word_frequency_functions.py)
|
||||||
* [Xgboost Classifier](machine_learning/xgboost_classifier.py)
|
* [Xgboost Classifier](machine_learning/xgboost_classifier.py)
|
||||||
* [Xgboost Regressor](machine_learning/xgboost_regressor.py)
|
* [Xgboost Regressor](machine_learning/xgboost_regressor.py)
|
||||||
* [Apriori Algorithm](machine_learning/apriori_algorithm.py)
|
|
||||||
|
|
||||||
## Maths
|
## Maths
|
||||||
* [Abs](maths/abs.py)
|
* [Abs](maths/abs.py)
|
||||||
|
|
143
dynamic_programming/matrix_chain_multiplication.py
Normal file
143
dynamic_programming/matrix_chain_multiplication.py
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
"""
|
||||||
|
Find the minimum number of multiplications needed to multiply chain of matrices.
|
||||||
|
Reference: https://www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/
|
||||||
|
|
||||||
|
The algorithm has interesting real-world applications. Example:
|
||||||
|
1. Image transformations in Computer Graphics as images are composed of matrix.
|
||||||
|
2. Solve complex polynomial equations in the field of algebra using least processing
|
||||||
|
power.
|
||||||
|
3. Calculate overall impact of macroeconomic decisions as economic equations involve a
|
||||||
|
number of variables.
|
||||||
|
4. Self-driving car navigation can be made more accurate as matrix multiplication can
|
||||||
|
accurately determine position and orientation of obstacles in short time.
|
||||||
|
|
||||||
|
Python doctests can be run with the following command:
|
||||||
|
python -m doctest -v matrix_chain_multiply.py
|
||||||
|
|
||||||
|
Given a sequence arr[] that represents chain of 2D matrices such that the dimension of
|
||||||
|
the ith matrix is arr[i-1]*arr[i].
|
||||||
|
So suppose arr = [40, 20, 30, 10, 30] means we have 4 matrices of dimensions
|
||||||
|
40*20, 20*30, 30*10 and 10*30.
|
||||||
|
|
||||||
|
matrix_chain_multiply() returns an integer denoting minimum number of multiplications to
|
||||||
|
multiply the chain.
|
||||||
|
|
||||||
|
We do not need to perform actual multiplication here.
|
||||||
|
We only need to decide the order in which to perform the multiplication.
|
||||||
|
|
||||||
|
Hints:
|
||||||
|
1. Number of multiplications (ie cost) to multiply 2 matrices
|
||||||
|
of size m*p and p*n is m*p*n.
|
||||||
|
2. Cost of matrix multiplication is associative ie (M1*M2)*M3 != M1*(M2*M3)
|
||||||
|
3. Matrix multiplication is not commutative. So, M1*M2 does not mean M2*M1 can be done.
|
||||||
|
4. To determine the required order, we can try different combinations.
|
||||||
|
So, this problem has overlapping sub-problems and can be solved using recursion.
|
||||||
|
We use Dynamic Programming for optimal time complexity.
|
||||||
|
|
||||||
|
Example input:
|
||||||
|
arr = [40, 20, 30, 10, 30]
|
||||||
|
output: 26000
|
||||||
|
"""
|
||||||
|
from collections.abc import Iterator
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from functools import cache
|
||||||
|
from sys import maxsize
|
||||||
|
|
||||||
|
|
||||||
|
def matrix_chain_multiply(arr: list[int]) -> int:
|
||||||
|
"""
|
||||||
|
Find the minimum number of multiplcations required to multiply the chain of matrices
|
||||||
|
|
||||||
|
Args:
|
||||||
|
arr: The input array of integers.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Minimum number of multiplications needed to multiply the chain
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> matrix_chain_multiply([1, 2, 3, 4, 3])
|
||||||
|
30
|
||||||
|
>>> matrix_chain_multiply([10])
|
||||||
|
0
|
||||||
|
>>> matrix_chain_multiply([10, 20])
|
||||||
|
0
|
||||||
|
>>> matrix_chain_multiply([19, 2, 19])
|
||||||
|
722
|
||||||
|
>>> matrix_chain_multiply(list(range(1, 100)))
|
||||||
|
323398
|
||||||
|
|
||||||
|
# >>> matrix_chain_multiply(list(range(1, 251)))
|
||||||
|
# 2626798
|
||||||
|
"""
|
||||||
|
if len(arr) < 2:
|
||||||
|
return 0
|
||||||
|
# initialising 2D dp matrix
|
||||||
|
n = len(arr)
|
||||||
|
dp = [[maxsize for j in range(n)] for i in range(n)]
|
||||||
|
# we want minimum cost of multiplication of matrices
|
||||||
|
# of dimension (i*k) and (k*j). This cost is arr[i-1]*arr[k]*arr[j].
|
||||||
|
for i in range(n - 1, 0, -1):
|
||||||
|
for j in range(i, n):
|
||||||
|
if i == j:
|
||||||
|
dp[i][j] = 0
|
||||||
|
continue
|
||||||
|
for k in range(i, j):
|
||||||
|
dp[i][j] = min(
|
||||||
|
dp[i][j], dp[i][k] + dp[k + 1][j] + arr[i - 1] * arr[k] * arr[j]
|
||||||
|
)
|
||||||
|
|
||||||
|
return dp[1][n - 1]
|
||||||
|
|
||||||
|
|
||||||
|
def matrix_chain_order(dims: list[int]) -> int:
|
||||||
|
"""
|
||||||
|
Source: https://en.wikipedia.org/wiki/Matrix_chain_multiplication
|
||||||
|
The dynamic programming solution is faster than cached the recursive solution and
|
||||||
|
can handle larger inputs.
|
||||||
|
>>> matrix_chain_order([1, 2, 3, 4, 3])
|
||||||
|
30
|
||||||
|
>>> matrix_chain_order([10])
|
||||||
|
0
|
||||||
|
>>> matrix_chain_order([10, 20])
|
||||||
|
0
|
||||||
|
>>> matrix_chain_order([19, 2, 19])
|
||||||
|
722
|
||||||
|
>>> matrix_chain_order(list(range(1, 100)))
|
||||||
|
323398
|
||||||
|
|
||||||
|
# >>> matrix_chain_order(list(range(1, 251))) # Max before RecursionError is raised
|
||||||
|
# 2626798
|
||||||
|
"""
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def a(i: int, j: int) -> int:
|
||||||
|
return min(
|
||||||
|
(a(i, k) + dims[i] * dims[k] * dims[j] + a(k, j) for k in range(i + 1, j)),
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
return a(0, len(dims) - 1)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def elapsed_time(msg: str) -> Iterator:
|
||||||
|
# print(f"Starting: {msg}")
|
||||||
|
from time import perf_counter_ns
|
||||||
|
|
||||||
|
start = perf_counter_ns()
|
||||||
|
yield
|
||||||
|
print(f"Finished: {msg} in {(perf_counter_ns() - start) / 10 ** 9} seconds.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import doctest
|
||||||
|
|
||||||
|
doctest.testmod()
|
||||||
|
with elapsed_time("matrix_chain_order"):
|
||||||
|
print(f"{matrix_chain_order(list(range(1, 251))) = }")
|
||||||
|
with elapsed_time("matrix_chain_multiply"):
|
||||||
|
print(f"{matrix_chain_multiply(list(range(1, 251))) = }")
|
||||||
|
with elapsed_time("matrix_chain_order"):
|
||||||
|
print(f"{matrix_chain_order(list(range(1, 251))) = }")
|
||||||
|
with elapsed_time("matrix_chain_multiply"):
|
||||||
|
print(f"{matrix_chain_multiply(list(range(1, 251))) = }")
|
Loading…
Reference in New Issue
Block a user