Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d610df1

Browse files
committedDec 29, 2024·
Fix sphinx/build_docs warnings for dynamic_programming
1 parent 94b3777 commit d610df1

13 files changed

+203
-196
lines changed
 

‎dynamic_programming/all_construct.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88

99
def all_construct(target: str, word_bank: list[str] | None = None) -> list[list[str]]:
1010
"""
11-
returns the list containing all the possible
12-
combinations a string(target) can be constructed from
13-
the given list of substrings(word_bank)
11+
returns the list containing all the possible
12+
combinations a string(`target`) can be constructed from
13+
the given list of substrings(`word_bank`)
14+
1415
>>> all_construct("hello", ["he", "l", "o"])
1516
[['he', 'l', 'l', 'o']]
1617
>>> all_construct("purple",["purp","p","ur","le","purpl"])

‎dynamic_programming/combination_sum_iv.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
"""
22
Question:
3-
You are given an array of distinct integers and you have to tell how many
4-
different ways of selecting the elements from the array are there such that
5-
the sum of chosen elements is equal to the target number tar.
3+
You are given an array of distinct integers and you have to tell how many
4+
different ways of selecting the elements from the array are there such that
5+
the sum of chosen elements is equal to the target number tar.
66
77
Example
88
99
Input:
10-
N = 3
11-
target = 5
12-
array = [1, 2, 5]
10+
* N = 3
11+
* target = 5
12+
* array = [1, 2, 5]
1313
1414
Output:
15-
9
15+
9
1616
1717
Approach:
18-
The basic idea is to go over recursively to find the way such that the sum
19-
of chosen elements is “tar”. For every element, we have two choices
20-
1. Include the element in our set of chosen elements.
21-
2. Don't include the element in our set of chosen elements.
18+
The basic idea is to go over recursively to find the way such that the sum
19+
of chosen elements is `target`. For every element, we have two choices
20+
21+
1. Include the element in our set of chosen elements.
22+
2. Don't include the element in our set of chosen elements.
2223
"""
2324

2425

‎dynamic_programming/fizz_buzz.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33

44
def fizz_buzz(number: int, iterations: int) -> str:
55
"""
6-
Plays FizzBuzz.
7-
Prints Fizz if number is a multiple of 3.
8-
Prints Buzz if its a multiple of 5.
9-
Prints FizzBuzz if its a multiple of both 3 and 5 or 15.
10-
Else Prints The Number Itself.
6+
| Plays FizzBuzz.
7+
| Prints Fizz if number is a multiple of ``3``.
8+
| Prints Buzz if its a multiple of ``5``.
9+
| Prints FizzBuzz if its a multiple of both ``3`` and ``5`` or ``15``.
10+
| Else Prints The Number Itself.
11+
1112
>>> fizz_buzz(1,7)
1213
'1 2 Fizz 4 Buzz Fizz 7 '
1314
>>> fizz_buzz(1,0)

‎dynamic_programming/knapsack.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def mf_knapsack(i, wt, val, j):
1111
"""
1212
This code involves the concept of memory functions. Here we solve the subproblems
1313
which are needed unlike the below example
14-
F is a 2D array with -1s filled up
14+
F is a 2D array with ``-1`` s filled up
1515
"""
1616
global f # a global dp table for knapsack
1717
if f[i][j] < 0:
@@ -45,22 +45,24 @@ def knapsack_with_example_solution(w: int, wt: list, val: list):
4545
the several possible optimal subsets.
4646
4747
Parameters
48-
---------
48+
----------
4949
50-
W: int, the total maximum weight for the given knapsack problem.
51-
wt: list, the vector of weights for all items where wt[i] is the weight
52-
of the i-th item.
53-
val: list, the vector of values for all items where val[i] is the value
54-
of the i-th item
50+
* `w`: int, the total maximum weight for the given knapsack problem.
51+
* `wt`: list, the vector of weights for all items where ``wt[i]`` is the weight
52+
of the ``i``-th item.
53+
* `val`: list, the vector of values for all items where ``val[i]`` is the value
54+
of the ``i``-th item
5555
5656
Returns
5757
-------
58-
optimal_val: float, the optimal value for the given knapsack problem
59-
example_optional_set: set, the indices of one of the optimal subsets
60-
which gave rise to the optimal value.
58+
59+
* `optimal_val`: float, the optimal value for the given knapsack problem
60+
* `example_optional_set`: set, the indices of one of the optimal subsets
61+
which gave rise to the optimal value.
6162
6263
Examples
63-
-------
64+
--------
65+
6466
>>> knapsack_with_example_solution(10, [1, 3, 5, 2], [10, 20, 100, 22])
6567
(142, {2, 3, 4})
6668
>>> knapsack_with_example_solution(6, [4, 3, 2, 3], [3, 2, 4, 4])
@@ -104,19 +106,18 @@ def _construct_solution(dp: list, wt: list, i: int, j: int, optimal_set: set):
104106
a filled DP table and the vector of weights
105107
106108
Parameters
107-
---------
108-
109-
dp: list of list, the table of a solved integer weight dynamic programming problem
109+
----------
110110
111-
wt: list or tuple, the vector of weights of the items
112-
i: int, the index of the item under consideration
113-
j: int, the current possible maximum weight
114-
optimal_set: set, the optimal subset so far. This gets modified by the function.
111+
* `dp`: list of list, the table of a solved integer weight dynamic programming problem
112+
* `wt`: list or tuple, the vector of weights of the items
113+
* `i`: int, the index of the item under consideration
114+
* `j`: int, the current possible maximum weight
115+
* `optimal_set`: set, the optimal subset so far. This gets modified by the function.
115116
116117
Returns
117118
-------
118-
None
119119
120+
``None``
120121
"""
121122
# for the current item i at a maximum weight j to be part of an optimal subset,
122123
# the optimal value at (i, j) must be greater than the optimal value at (i-1, j).

‎dynamic_programming/longest_common_substring.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
"""
2-
Longest Common Substring Problem Statement: Given two sequences, find the
3-
longest common substring present in both of them. A substring is
4-
necessarily continuous.
5-
Example: "abcdef" and "xabded" have two longest common substrings, "ab" or "de".
6-
Therefore, algorithm should return any one of them.
2+
Longest Common Substring Problem Statement:
3+
Given two sequences, find the
4+
longest common substring present in both of them. A substring is
5+
necessarily continuous.
6+
7+
Example:
8+
``abcdef`` and ``xabded`` have two longest common substrings, ``ab`` or ``de``.
9+
Therefore, algorithm should return any one of them.
710
"""
811

912

1013
def longest_common_substring(text1: str, text2: str) -> str:
1114
"""
1215
Finds the longest common substring between two strings.
16+
1317
>>> longest_common_substring("", "")
1418
''
1519
>>> longest_common_substring("a","")

‎dynamic_programming/longest_increasing_subsequence.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
This is a pure Python implementation of Dynamic Programming solution to the longest
55
increasing subsequence of a given sequence.
66
7-
The problem is :
8-
Given an array, to find the longest and increasing sub-array in that given array and
9-
return it.
10-
Example: [10, 22, 9, 33, 21, 50, 41, 60, 80] as input will return
11-
[10, 22, 33, 41, 60, 80] as output
7+
The problem is:
8+
Given an array, to find the longest and increasing sub-array in that given array and
9+
return it.
10+
11+
Example:
12+
``[10, 22, 9, 33, 21, 50, 41, 60, 80]`` as input will return
13+
``[10, 22, 33, 41, 60, 80]`` as output
1214
"""
1315

1416
from __future__ import annotations
@@ -17,6 +19,7 @@
1719
def longest_subsequence(array: list[int]) -> list[int]: # This function is recursive
1820
"""
1921
Some examples
22+
2023
>>> longest_subsequence([10, 22, 9, 33, 21, 50, 41, 60, 80])
2124
[10, 22, 33, 41, 60, 80]
2225
>>> longest_subsequence([4, 8, 7, 5, 1, 12, 2, 3, 9])

‎dynamic_programming/matrix_chain_multiplication.py

Lines changed: 49 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,47 @@
11
"""
2-
Find the minimum number of multiplications needed to multiply chain of matrices.
3-
Reference: https://www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/
4-
5-
The algorithm has interesting real-world applications. Example:
6-
1. Image transformations in Computer Graphics as images are composed of matrix.
7-
2. Solve complex polynomial equations in the field of algebra using least processing
8-
power.
9-
3. Calculate overall impact of macroeconomic decisions as economic equations involve a
10-
number of variables.
11-
4. Self-driving car navigation can be made more accurate as matrix multiplication can
12-
accurately determine position and orientation of obstacles in short time.
13-
14-
Python doctests can be run with the following command:
15-
python -m doctest -v matrix_chain_multiply.py
16-
17-
Given a sequence arr[] that represents chain of 2D matrices such that the dimension of
18-
the ith matrix is arr[i-1]*arr[i].
19-
So suppose arr = [40, 20, 30, 10, 30] means we have 4 matrices of dimensions
20-
40*20, 20*30, 30*10 and 10*30.
21-
22-
matrix_chain_multiply() returns an integer denoting minimum number of multiplications to
2+
| Find the minimum number of multiplications needed to multiply chain of matrices.
3+
| Reference: https://www.geeksforgeeks.org/matrix-chain-multiplication-dp-8/
4+
5+
The algorithm has interesting real-world applications.
6+
7+
Example:
8+
1. Image transformations in Computer Graphics as images are composed of matrix.
9+
2. Solve complex polynomial equations in the field of algebra using least processing
10+
power.
11+
3. Calculate overall impact of macroeconomic decisions as economic equations involve a
12+
number of variables.
13+
4. Self-driving car navigation can be made more accurate as matrix multiplication can
14+
accurately determine position and orientation of obstacles in short time.
15+
16+
Python doctests can be run with the following command::
17+
18+
python -m doctest -v matrix_chain_multiply.py
19+
20+
Given a sequence ``arr[]`` that represents chain of 2D matrices such that the dimension of
21+
the ``i`` th matrix is ``arr[i-1]*arr[i]``.
22+
So suppose ``arr = [40, 20, 30, 10, 30]`` means we have ``4`` matrices of dimensions
23+
``40*20``, ``20*30``, ``30*10`` and ``10*30``.
24+
25+
``matrix_chain_multiply()`` returns an integer denoting minimum number of multiplications to
2326
multiply the chain.
2427
2528
We do not need to perform actual multiplication here.
2629
We only need to decide the order in which to perform the multiplication.
2730
2831
Hints:
29-
1. Number of multiplications (ie cost) to multiply 2 matrices
30-
of size m*p and p*n is m*p*n.
31-
2. Cost of matrix multiplication is associative ie (M1*M2)*M3 != M1*(M2*M3)
32-
3. Matrix multiplication is not commutative. So, M1*M2 does not mean M2*M1 can be done.
33-
4. To determine the required order, we can try different combinations.
32+
1. Number of multiplications (ie cost) to multiply ``2`` matrices
33+
of size ``m*p`` and ``p*n`` is ``m*p*n``.
34+
2. Cost of matrix multiplication is not associative ie ``(M1*M2)*M3 != M1*(M2*M3)``
35+
3. Matrix multiplication is not commutative. So, ``M1*M2`` does not mean ``M2*M1`` can be done.
36+
4. To determine the required order, we can try different combinations.
37+
3438
So, this problem has overlapping sub-problems and can be solved using recursion.
3539
We use Dynamic Programming for optimal time complexity.
3640
3741
Example input:
38-
arr = [40, 20, 30, 10, 30]
39-
output: 26000
42+
``arr = [40, 20, 30, 10, 30]``
43+
output:
44+
``26000``
4045
"""
4146

4247
from collections.abc import Iterator
@@ -50,25 +55,25 @@ def matrix_chain_multiply(arr: list[int]) -> int:
5055
Find the minimum number of multiplcations required to multiply the chain of matrices
5156
5257
Args:
53-
arr: The input array of integers.
58+
`arr`: The input array of integers.
5459
5560
Returns:
5661
Minimum number of multiplications needed to multiply the chain
5762
5863
Examples:
59-
>>> matrix_chain_multiply([1, 2, 3, 4, 3])
60-
30
61-
>>> matrix_chain_multiply([10])
62-
0
63-
>>> matrix_chain_multiply([10, 20])
64-
0
65-
>>> matrix_chain_multiply([19, 2, 19])
66-
722
67-
>>> matrix_chain_multiply(list(range(1, 100)))
68-
323398
69-
70-
# >>> matrix_chain_multiply(list(range(1, 251)))
71-
# 2626798
64+
65+
>>> matrix_chain_multiply([1, 2, 3, 4, 3])
66+
30
67+
>>> matrix_chain_multiply([10])
68+
0
69+
>>> matrix_chain_multiply([10, 20])
70+
0
71+
>>> matrix_chain_multiply([19, 2, 19])
72+
722
73+
>>> matrix_chain_multiply(list(range(1, 100)))
74+
323398
75+
# >>> matrix_chain_multiply(list(range(1, 251)))
76+
# 2626798
7277
"""
7378
if len(arr) < 2:
7479
return 0
@@ -93,8 +98,10 @@ def matrix_chain_multiply(arr: list[int]) -> int:
9398
def matrix_chain_order(dims: list[int]) -> int:
9499
"""
95100
Source: https://en.wikipedia.org/wiki/Matrix_chain_multiplication
101+
96102
The dynamic programming solution is faster than cached the recursive solution and
97103
can handle larger inputs.
104+
98105
>>> matrix_chain_order([1, 2, 3, 4, 3])
99106
30
100107
>>> matrix_chain_order([10])
@@ -105,7 +112,6 @@ def matrix_chain_order(dims: list[int]) -> int:
105112
722
106113
>>> matrix_chain_order(list(range(1, 100)))
107114
323398
108-
109115
# >>> matrix_chain_order(list(range(1, 251))) # Max before RecursionError is raised
110116
# 2626798
111117
"""

‎dynamic_programming/max_product_subarray.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
def max_product_subarray(numbers: list[int]) -> int:
22
"""
33
Returns the maximum product that can be obtained by multiplying a
4-
contiguous subarray of the given integer list `nums`.
4+
contiguous subarray of the given integer list `numbers`.
55
66
Example:
7+
78
>>> max_product_subarray([2, 3, -2, 4])
89
6
910
>>> max_product_subarray((-2, 0, -1))

‎dynamic_programming/minimum_squares_to_represent_a_number.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
def minimum_squares_to_represent_a_number(number: int) -> int:
66
"""
77
Count the number of minimum squares to represent a number
8+
89
>>> minimum_squares_to_represent_a_number(25)
910
1
1011
>>> minimum_squares_to_represent_a_number(37)

‎dynamic_programming/regex_match.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
"""
22
Regex matching check if a text matches pattern or not.
33
Pattern:
4-
'.' Matches any single character.
5-
'*' Matches zero or more of the preceding element.
4+
5+
1. ``.`` Matches any single character.
6+
2. ``*`` Matches zero or more of the preceding element.
7+
68
More info:
79
https://medium.com/trick-the-interviwer/regular-expression-matching-9972eb74c03
810
"""
@@ -12,12 +14,12 @@ def recursive_match(text: str, pattern: str) -> bool:
1214
"""
1315
Recursive matching algorithm.
1416
15-
Time complexity: O(2 ^ (|text| + |pattern|))
16-
Space complexity: Recursion depth is O(|text| + |pattern|).
17+
| Time complexity: O(2^(\|text\| + \|pattern\|))
18+
| Space complexity: Recursion depth is O(\|text\| + \|pattern\|).
1719
1820
:param text: Text to match.
1921
:param pattern: Pattern to match.
20-
:return: True if text matches pattern, False otherwise.
22+
:return: ``True`` if `text` matches `pattern`, ``False`` otherwise.
2123
2224
>>> recursive_match('abc', 'a.c')
2325
True
@@ -51,12 +53,12 @@ def dp_match(text: str, pattern: str) -> bool:
5153
"""
5254
Dynamic programming matching algorithm.
5355
54-
Time complexity: O(|text| * |pattern|)
55-
Space complexity: O(|text| * |pattern|)
56+
| Time complexity: O(\|text\| * \|pattern\|)
57+
| Space complexity: O(\|text\| * \|pattern\|)
5658
5759
:param text: Text to match.
5860
:param pattern: Pattern to match.
59-
:return: True if text matches pattern, False otherwise.
61+
:return: ``True`` if `text` matches `pattern`, ``False`` otherwise.
6062
6163
>>> dp_match('abc', 'a.c')
6264
True

‎dynamic_programming/rod_cutting.py

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
This module provides two implementations for the rod-cutting problem:
3-
1. A naive recursive implementation which has an exponential runtime
4-
2. Two dynamic programming implementations which have quadratic runtime
3+
1. A naive recursive implementation which has an exponential runtime
4+
2. Two dynamic programming implementations which have quadratic runtime
55
66
The rod-cutting problem is the problem of finding the maximum possible revenue
77
obtainable from a rod of length ``n`` given a list of prices for each integral piece
@@ -20,18 +20,21 @@ def naive_cut_rod_recursive(n: int, prices: list):
2020
Runtime: O(2^n)
2121
2222
Arguments
23-
-------
24-
n: int, the length of the rod
25-
prices: list, the prices for each piece of rod. ``p[i-i]`` is the
26-
price for a rod of length ``i``
23+
---------
24+
25+
* `n`: int, the length of the rod
26+
* `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the
27+
price for a rod of length ``i``
2728
2829
Returns
2930
-------
30-
The maximum revenue obtainable for a rod of length n given the list of prices
31+
32+
The maximum revenue obtainable for a rod of length `n` given the list of prices
3133
for each piece.
3234
3335
Examples
3436
--------
37+
3538
>>> naive_cut_rod_recursive(4, [1, 5, 8, 9])
3639
10
3740
>>> naive_cut_rod_recursive(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30])
@@ -54,28 +57,30 @@ def top_down_cut_rod(n: int, prices: list):
5457
"""
5558
Constructs a top-down dynamic programming solution for the rod-cutting
5659
problem via memoization. This function serves as a wrapper for
57-
_top_down_cut_rod_recursive
60+
``_top_down_cut_rod_recursive``
5861
5962
Runtime: O(n^2)
6063
6164
Arguments
62-
--------
63-
n: int, the length of the rod
64-
prices: list, the prices for each piece of rod. ``p[i-i]`` is the
65-
price for a rod of length ``i``
65+
---------
6666
67-
Note
68-
----
69-
For convenience and because Python's lists using 0-indexing, length(max_rev) =
70-
n + 1, to accommodate for the revenue obtainable from a rod of length 0.
67+
* `n`: int, the length of the rod
68+
* `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the
69+
price for a rod of length ``i``
70+
71+
.. note::
72+
For convenience and because Python's lists using ``0``-indexing, ``length(max_rev) =
73+
n + 1``, to accommodate for the revenue obtainable from a rod of length ``0```.
7174
7275
Returns
7376
-------
74-
The maximum revenue obtainable for a rod of length n given the list of prices
77+
78+
The maximum revenue obtainable for a rod of length `n` given the list of prices
7579
for each piece.
7680
7781
Examples
78-
-------
82+
--------
83+
7984
>>> top_down_cut_rod(4, [1, 5, 8, 9])
8085
10
8186
>>> top_down_cut_rod(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30])
@@ -94,16 +99,18 @@ def _top_down_cut_rod_recursive(n: int, prices: list, max_rev: list):
9499
Runtime: O(n^2)
95100
96101
Arguments
97-
--------
98-
n: int, the length of the rod
99-
prices: list, the prices for each piece of rod. ``p[i-i]`` is the
100-
price for a rod of length ``i``
101-
max_rev: list, the computed maximum revenue for a piece of rod.
102-
``max_rev[i]`` is the maximum revenue obtainable for a rod of length ``i``
102+
---------
103+
104+
* `n`: int, the length of the rod
105+
* `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the
106+
price for a rod of length ``i``
107+
* `max_rev`: list, the computed maximum revenue for a piece of rod.
108+
``max_rev[i]`` is the maximum revenue obtainable for a rod of length ``i``
103109
104110
Returns
105111
-------
106-
The maximum revenue obtainable for a rod of length n given the list of prices
112+
113+
The maximum revenue obtainable for a rod of length `n` given the list of prices
107114
for each piece.
108115
"""
109116
if max_rev[n] >= 0:
@@ -130,18 +137,21 @@ def bottom_up_cut_rod(n: int, prices: list):
130137
Runtime: O(n^2)
131138
132139
Arguments
133-
----------
134-
n: int, the maximum length of the rod.
135-
prices: list, the prices for each piece of rod. ``p[i-i]`` is the
136-
price for a rod of length ``i``
140+
---------
141+
142+
* `n`: int, the maximum length of the rod.
143+
* `prices`: list, the prices for each piece of rod. ``p[i-i]`` is the
144+
price for a rod of length ``i``
137145
138146
Returns
139147
-------
140-
The maximum revenue obtainable from cutting a rod of length n given
148+
149+
The maximum revenue obtainable from cutting a rod of length `n` given
141150
the prices for each piece of rod p.
142151
143152
Examples
144-
-------
153+
--------
154+
145155
>>> bottom_up_cut_rod(4, [1, 5, 8, 9])
146156
10
147157
>>> bottom_up_cut_rod(10, [1, 5, 8, 9, 10, 17, 17, 20, 24, 30])
@@ -168,13 +178,12 @@ def _enforce_args(n: int, prices: list):
168178
"""
169179
Basic checks on the arguments to the rod-cutting algorithms
170180
171-
n: int, the length of the rod
172-
prices: list, the price list for each piece of rod.
173-
174-
Throws ValueError:
181+
* `n`: int, the length of the rod
182+
* `prices`: list, the price list for each piece of rod.
175183
176-
if n is negative or there are fewer items in the price list than the length of
177-
the rod
184+
Throws ``ValueError``:
185+
if `n` is negative or there are fewer items in the price list than the length of
186+
the rod
178187
"""
179188
if n < 0:
180189
msg = f"n must be greater than or equal to 0. Got n = {n}"

‎dynamic_programming/subset_generation.py

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,41 @@
11
def subset_combinations(elements: list[int], n: int) -> list:
22
"""
33
Compute n-element combinations from a given list using dynamic programming.
4+
45
Args:
5-
elements: The list of elements from which combinations will be generated.
6-
n: The number of elements in each combination.
6+
* `elements`: The list of elements from which combinations will be generated.
7+
* `n`: The number of elements in each combination.
8+
79
Returns:
8-
A list of tuples, each representing a combination of n elements.
9-
>>> subset_combinations(elements=[10, 20, 30, 40], n=2)
10-
[(10, 20), (10, 30), (10, 40), (20, 30), (20, 40), (30, 40)]
11-
>>> subset_combinations(elements=[1, 2, 3], n=1)
12-
[(1,), (2,), (3,)]
13-
>>> subset_combinations(elements=[1, 2, 3], n=3)
14-
[(1, 2, 3)]
15-
>>> subset_combinations(elements=[42], n=1)
16-
[(42,)]
17-
>>> subset_combinations(elements=[6, 7, 8, 9], n=4)
18-
[(6, 7, 8, 9)]
19-
>>> subset_combinations(elements=[10, 20, 30, 40, 50], n=0)
20-
[()]
21-
>>> subset_combinations(elements=[1, 2, 3, 4], n=2)
22-
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
23-
>>> subset_combinations(elements=[1, 'apple', 3.14], n=2)
24-
[(1, 'apple'), (1, 3.14), ('apple', 3.14)]
25-
>>> subset_combinations(elements=['single'], n=0)
26-
[()]
27-
>>> subset_combinations(elements=[], n=9)
28-
[]
29-
>>> from itertools import combinations
30-
>>> all(subset_combinations(items, n) == list(combinations(items, n))
31-
... for items, n in (
32-
... ([10, 20, 30, 40], 2), ([1, 2, 3], 1), ([1, 2, 3], 3), ([42], 1),
33-
... ([6, 7, 8, 9], 4), ([10, 20, 30, 40, 50], 1), ([1, 2, 3, 4], 2),
34-
... ([1, 'apple', 3.14], 2), (['single'], 0), ([], 9)))
35-
True
10+
A list of tuples, each representing a combination of `n` elements.
11+
12+
>>> subset_combinations(elements=[10, 20, 30, 40], n=2)
13+
[(10, 20), (10, 30), (10, 40), (20, 30), (20, 40), (30, 40)]
14+
>>> subset_combinations(elements=[1, 2, 3], n=1)
15+
[(1,), (2,), (3,)]
16+
>>> subset_combinations(elements=[1, 2, 3], n=3)
17+
[(1, 2, 3)]
18+
>>> subset_combinations(elements=[42], n=1)
19+
[(42,)]
20+
>>> subset_combinations(elements=[6, 7, 8, 9], n=4)
21+
[(6, 7, 8, 9)]
22+
>>> subset_combinations(elements=[10, 20, 30, 40, 50], n=0)
23+
[()]
24+
>>> subset_combinations(elements=[1, 2, 3, 4], n=2)
25+
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
26+
>>> subset_combinations(elements=[1, 'apple', 3.14], n=2)
27+
[(1, 'apple'), (1, 3.14), ('apple', 3.14)]
28+
>>> subset_combinations(elements=['single'], n=0)
29+
[()]
30+
>>> subset_combinations(elements=[], n=9)
31+
[]
32+
>>> from itertools import combinations
33+
>>> all(subset_combinations(items, n) == list(combinations(items, n))
34+
... for items, n in (
35+
... ([10, 20, 30, 40], 2), ([1, 2, 3], 1), ([1, 2, 3], 3), ([42], 1),
36+
... ([6, 7, 8, 9], 4), ([10, 20, 30, 40, 50], 1), ([1, 2, 3, 4], 2),
37+
... ([1, 'apple', 3.14], 2), (['single'], 0), ([], 9)))
38+
True
3639
"""
3740
r = len(elements)
3841
if n > r:

‎dynamic_programming/viterbi.py

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ def viterbi(
1111
"""
1212
Viterbi Algorithm, to find the most likely path of
1313
states from the start and the expected output.
14+
1415
https://en.wikipedia.org/wiki/Viterbi_algorithm
15-
sdafads
16+
1617
Wikipedia example
18+
1719
>>> observations = ["normal", "cold", "dizzy"]
1820
>>> states = ["Healthy", "Fever"]
1921
>>> start_p = {"Healthy": 0.6, "Fever": 0.4}
@@ -27,97 +29,78 @@ def viterbi(
2729
... }
2830
>>> viterbi(observations, states, start_p, trans_p, emit_p)
2931
['Healthy', 'Healthy', 'Fever']
30-
3132
>>> viterbi((), states, start_p, trans_p, emit_p)
3233
Traceback (most recent call last):
3334
...
3435
ValueError: There's an empty parameter
35-
3636
>>> viterbi(observations, (), start_p, trans_p, emit_p)
3737
Traceback (most recent call last):
3838
...
3939
ValueError: There's an empty parameter
40-
4140
>>> viterbi(observations, states, {}, trans_p, emit_p)
4241
Traceback (most recent call last):
4342
...
4443
ValueError: There's an empty parameter
45-
4644
>>> viterbi(observations, states, start_p, {}, emit_p)
4745
Traceback (most recent call last):
4846
...
4947
ValueError: There's an empty parameter
50-
5148
>>> viterbi(observations, states, start_p, trans_p, {})
5249
Traceback (most recent call last):
5350
...
5451
ValueError: There's an empty parameter
55-
5652
>>> viterbi("invalid", states, start_p, trans_p, emit_p)
5753
Traceback (most recent call last):
5854
...
5955
ValueError: observations_space must be a list
60-
6156
>>> viterbi(["valid", 123], states, start_p, trans_p, emit_p)
6257
Traceback (most recent call last):
6358
...
6459
ValueError: observations_space must be a list of strings
65-
6660
>>> viterbi(observations, "invalid", start_p, trans_p, emit_p)
6761
Traceback (most recent call last):
6862
...
6963
ValueError: states_space must be a list
70-
7164
>>> viterbi(observations, ["valid", 123], start_p, trans_p, emit_p)
7265
Traceback (most recent call last):
7366
...
7467
ValueError: states_space must be a list of strings
75-
7668
>>> viterbi(observations, states, "invalid", trans_p, emit_p)
7769
Traceback (most recent call last):
7870
...
7971
ValueError: initial_probabilities must be a dict
80-
8172
>>> viterbi(observations, states, {2:2}, trans_p, emit_p)
8273
Traceback (most recent call last):
8374
...
8475
ValueError: initial_probabilities all keys must be strings
85-
8676
>>> viterbi(observations, states, {"a":2}, trans_p, emit_p)
8777
Traceback (most recent call last):
8878
...
8979
ValueError: initial_probabilities all values must be float
90-
9180
>>> viterbi(observations, states, start_p, "invalid", emit_p)
9281
Traceback (most recent call last):
9382
...
9483
ValueError: transition_probabilities must be a dict
95-
9684
>>> viterbi(observations, states, start_p, {"a":2}, emit_p)
9785
Traceback (most recent call last):
9886
...
9987
ValueError: transition_probabilities all values must be dict
100-
10188
>>> viterbi(observations, states, start_p, {2:{2:2}}, emit_p)
10289
Traceback (most recent call last):
10390
...
10491
ValueError: transition_probabilities all keys must be strings
105-
10692
>>> viterbi(observations, states, start_p, {"a":{2:2}}, emit_p)
10793
Traceback (most recent call last):
10894
...
10995
ValueError: transition_probabilities all keys must be strings
110-
11196
>>> viterbi(observations, states, start_p, {"a":{"b":2}}, emit_p)
11297
Traceback (most recent call last):
11398
...
11499
ValueError: transition_probabilities nested dictionary all values must be float
115-
116100
>>> viterbi(observations, states, start_p, trans_p, "invalid")
117101
Traceback (most recent call last):
118102
...
119103
ValueError: emission_probabilities must be a dict
120-
121104
>>> viterbi(observations, states, start_p, trans_p, None)
122105
Traceback (most recent call last):
123106
...
@@ -213,7 +196,6 @@ def _validation(
213196
... "Fever": {"normal": 0.1, "cold": 0.3, "dizzy": 0.6},
214197
... }
215198
>>> _validation(observations, states, start_p, trans_p, emit_p)
216-
217199
>>> _validation([], states, start_p, trans_p, emit_p)
218200
Traceback (most recent call last):
219201
...
@@ -242,7 +224,6 @@ def _validate_not_empty(
242224
"""
243225
>>> _validate_not_empty(["a"], ["b"], {"c":0.5},
244226
... {"d": {"e": 0.6}}, {"f": {"g": 0.7}})
245-
246227
>>> _validate_not_empty(["a"], ["b"], {"c":0.5}, {}, {"f": {"g": 0.7}})
247228
Traceback (most recent call last):
248229
...
@@ -267,12 +248,10 @@ def _validate_not_empty(
267248
def _validate_lists(observations_space: Any, states_space: Any) -> None:
268249
"""
269250
>>> _validate_lists(["a"], ["b"])
270-
271251
>>> _validate_lists(1234, ["b"])
272252
Traceback (most recent call last):
273253
...
274254
ValueError: observations_space must be a list
275-
276255
>>> _validate_lists(["a"], [3])
277256
Traceback (most recent call last):
278257
...
@@ -285,7 +264,6 @@ def _validate_lists(observations_space: Any, states_space: Any) -> None:
285264
def _validate_list(_object: Any, var_name: str) -> None:
286265
"""
287266
>>> _validate_list(["a"], "mock_name")
288-
289267
>>> _validate_list("a", "mock_name")
290268
Traceback (most recent call last):
291269
...
@@ -294,7 +272,6 @@ def _validate_list(_object: Any, var_name: str) -> None:
294272
Traceback (most recent call last):
295273
...
296274
ValueError: mock_name must be a list of strings
297-
298275
"""
299276
if not isinstance(_object, list):
300277
msg = f"{var_name} must be a list"
@@ -313,7 +290,6 @@ def _validate_dicts(
313290
) -> None:
314291
"""
315292
>>> _validate_dicts({"c":0.5}, {"d": {"e": 0.6}}, {"f": {"g": 0.7}})
316-
317293
>>> _validate_dicts("invalid", {"d": {"e": 0.6}}, {"f": {"g": 0.7}})
318294
Traceback (most recent call last):
319295
...
@@ -339,7 +315,6 @@ def _validate_dicts(
339315
def _validate_nested_dict(_object: Any, var_name: str) -> None:
340316
"""
341317
>>> _validate_nested_dict({"a":{"b": 0.5}}, "mock_name")
342-
343318
>>> _validate_nested_dict("invalid", "mock_name")
344319
Traceback (most recent call last):
345320
...
@@ -367,7 +342,6 @@ def _validate_dict(
367342
) -> None:
368343
"""
369344
>>> _validate_dict({"b": 0.5}, "mock_name", float)
370-
371345
>>> _validate_dict("invalid", "mock_name", float)
372346
Traceback (most recent call last):
373347
...

0 commit comments

Comments
 (0)
Please sign in to comment.