mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-23 21:11: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)
|
||||
* [Prefix Sum](data_structures/arrays/prefix_sum.py)
|
||||
* [Product Sum](data_structures/arrays/product_sum.py)
|
||||
* [Sparse Table](data_structures/arrays/sparse_table.py)
|
||||
* Binary Tree
|
||||
* [Avl Tree](data_structures/binary_tree/avl_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 Palindromic Subsequence](dynamic_programming/longest_palindromic_subsequence.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)
|
||||
* [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py)
|
||||
* [Max Product Subarray](dynamic_programming/max_product_subarray.py)
|
||||
|
@ -370,6 +372,7 @@
|
|||
* [Builtin Voltage](electronics/builtin_voltage.py)
|
||||
* [Carrier Concentration](electronics/carrier_concentration.py)
|
||||
* [Charging Capacitor](electronics/charging_capacitor.py)
|
||||
* [Charging Inductor](electronics/charging_inductor.py)
|
||||
* [Circular Convolution](electronics/circular_convolution.py)
|
||||
* [Coulombs Law](electronics/coulombs_law.py)
|
||||
* [Electric Conductivity](electronics/electric_conductivity.py)
|
||||
|
@ -524,6 +527,7 @@
|
|||
* [Simplex](linear_programming/simplex.py)
|
||||
|
||||
## Machine Learning
|
||||
* [Apriori Algorithm](machine_learning/apriori_algorithm.py)
|
||||
* [Astar](machine_learning/astar.py)
|
||||
* [Data Transformations](machine_learning/data_transformations.py)
|
||||
* [Decision Tree](machine_learning/decision_tree.py)
|
||||
|
@ -554,7 +558,6 @@
|
|||
* [Word Frequency Functions](machine_learning/word_frequency_functions.py)
|
||||
* [Xgboost Classifier](machine_learning/xgboost_classifier.py)
|
||||
* [Xgboost Regressor](machine_learning/xgboost_regressor.py)
|
||||
* [Apriori Algorithm](machine_learning/apriori_algorithm.py)
|
||||
|
||||
## Maths
|
||||
* [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