Skip to content

Commit 6d8852a

Browse files
committed
adding a combinatorics folder with basic algorithms
1 parent 51c5c87 commit 6d8852a

File tree

3 files changed

+313
-0
lines changed

3 files changed

+313
-0
lines changed

combinatorics/combinations.py

+226
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
from math import factorial, comb
2+
3+
"""
4+
https://en.wikipedia.org/wiki/Combination
5+
"""
6+
7+
8+
def validate_elements_count(
9+
total_elements_count: int, selected_elements_count: int
10+
) -> None:
11+
# If either of the conditions are true, the function is being asked
12+
# to calculate a factorial of a negative number, which is not possible
13+
if total_elements_count < selected_elements_count or selected_elements_count < 0:
14+
raise ValueError(
15+
"Please enter positive integers for total_elements_count and selected_elements_count where total_elements_count >= selected_elements_count"
16+
)
17+
18+
19+
def combinations_iterative(
20+
total_elements_count: int, selected_elements_count: int
21+
) -> int:
22+
"""
23+
Returns the number of different combinations of selected_elements_count length which can
24+
be made from total_elements_count values, where total_elements_count >= selected_elements_count.
25+
26+
Examples:
27+
>>> combinations_iterative(10,5)
28+
252
29+
30+
>>> combinations_iterative(6,3)
31+
20
32+
33+
>>> combinations_iterative(20,5)
34+
15504
35+
36+
>>> combinations_iterative(52, 5)
37+
2598960
38+
39+
>>> combinations_iterative(0, 0)
40+
1
41+
42+
>>> combinations_iterative(-4, -5)
43+
Traceback (most recent call last):
44+
...
45+
ValueError: Please enter positive integers for total_elements_count and selected_elements_count where total_elements_count >= selected_elements_count
46+
"""
47+
validate_elements_count(total_elements_count, selected_elements_count)
48+
combinations_count = 1
49+
for processing_at_this_moment_elements_count in range(selected_elements_count):
50+
combinations_count *= (
51+
total_elements_count - processing_at_this_moment_elements_count
52+
)
53+
combinations_count //= processing_at_this_moment_elements_count + 1
54+
return combinations_count
55+
56+
57+
def multiset_combinations(
58+
total_elements_count: int, selected_elements_count: int
59+
) -> int:
60+
"""
61+
Returns the number of different combinations of selected_elements_count length which can
62+
be made from total_elements_count values, where total_elements_count >= selected_elements_count.
63+
64+
Examples:
65+
>>> multiset_combinations(10,5)
66+
2002
67+
68+
>>> multiset_combinations(6,3)
69+
56
70+
71+
>>> multiset_combinations(20,5)
72+
42504
73+
74+
>>> multiset_combinations(52, 5)
75+
3819816
76+
77+
>>> multiset_combinations(0, 0)
78+
Traceback (most recent call last):
79+
...
80+
ValueError: n must be a non-negative integer
81+
82+
>>> multiset_combinations(-4, -5)
83+
Traceback (most recent call last):
84+
...
85+
ValueError: n must be a non-negative integer
86+
"""
87+
88+
return comb(
89+
total_elements_count + selected_elements_count - 1, selected_elements_count
90+
)
91+
92+
93+
def combinations_formula(
94+
total_elements_count: int, selected_elements_count: int
95+
) -> int:
96+
"""
97+
Returns the number of different combinations of selected_elements_count length which can
98+
be made from total_elements_count values, where total_elements_count >= selected_elements_count.
99+
100+
Examples:
101+
>>> combinations_formula(10,5)
102+
252
103+
104+
>>> combinations_formula(6,3)
105+
20
106+
107+
>>> combinations_formula(20,5)
108+
15504
109+
110+
>>> combinations_formula(52, 5)
111+
2598960
112+
113+
>>> combinations_formula(0, 0)
114+
1
115+
116+
>>> combinations_formula(-4, -5)
117+
Traceback (most recent call last):
118+
...
119+
ValueError: Please enter positive integers for total_elements_count and selected_elements_count where total_elements_count >= selected_elements_count
120+
"""
121+
validate_elements_count(total_elements_count, selected_elements_count)
122+
remaining_elements_count = total_elements_count - selected_elements_count
123+
return int(
124+
factorial(total_elements_count)
125+
/ (factorial(selected_elements_count) * factorial(remaining_elements_count))
126+
)
127+
128+
129+
def combinations_with_repetitions(
130+
total_elements_count: int, selected_elements_count: int
131+
) -> int:
132+
"""
133+
Returns the number of different combinations of selected_elements_count length which can
134+
be made from total_elements_count values, where total_elements_count >= selected_elements_count.
135+
136+
Examples:
137+
>>> combinations_with_repetitions(10,5)
138+
2002
139+
140+
>>> combinations_with_repetitions(6,3)
141+
56
142+
143+
>>> combinations_with_repetitions(20,5)
144+
42504
145+
146+
>>> combinations_with_repetitions(52, 5)
147+
3819816
148+
149+
>>> combinations_with_repetitions(0, 0)
150+
1
151+
152+
>>> combinations_with_repetitions(-4, -5)
153+
Traceback (most recent call last):
154+
...
155+
ValueError: Please enter positive integers for total_elements_count and selected_elements_count where total_elements_count >= selected_elements_count
156+
"""
157+
validate_elements_count(total_elements_count, selected_elements_count)
158+
if total_elements_count + selected_elements_count == 0:
159+
return 1
160+
return int(
161+
factorial(total_elements_count + selected_elements_count - 1)
162+
/ (factorial(selected_elements_count) * factorial(total_elements_count - 1))
163+
)
164+
165+
166+
def permutations(total_elements_count: int, selected_elements_count: int) -> int:
167+
"""
168+
https://en.wikipedia.org/wiki/Permutation
169+
170+
Examples:
171+
>>> permutations(10,5)
172+
30240
173+
174+
>>> permutations(6,3)
175+
120
176+
177+
>>> permutations(20,5)
178+
1860480
179+
180+
>>> permutations(52, 5)
181+
311875200
182+
183+
>>> permutations(0, 0)
184+
1
185+
186+
>>> permutations(-4, -5)
187+
Traceback (most recent call last):
188+
...
189+
ValueError: Please enter positive integers for total_elements_count and selected_elements_count where total_elements_count >= selected_elements_count
190+
"""
191+
validate_elements_count(total_elements_count, selected_elements_count)
192+
remaining_elements_count = total_elements_count - selected_elements_count
193+
return int(factorial(total_elements_count) / factorial(remaining_elements_count))
194+
195+
196+
def possible_selections(total_elements_count: int, selected_elements_count: int) -> int:
197+
"""
198+
https://en.wikipedia.org/wiki/Permutation
199+
200+
Examples:
201+
>>> possible_selections(10,5)
202+
100000
203+
204+
>>> possible_selections(6,3)
205+
216
206+
207+
>>> possible_selections(20,5)
208+
3200000
209+
210+
>>> possible_selections(52, 5)
211+
380204032
212+
213+
>>> possible_selections(0, 0)
214+
1
215+
216+
>>> possible_selections(-4, -5)
217+
Traceback (most recent call last):
218+
...
219+
ValueError: Please enter positive integers for total_elements_count and selected_elements_count where total_elements_count >= selected_elements_count
220+
"""
221+
validate_elements_count(total_elements_count, selected_elements_count)
222+
return int(total_elements_count**selected_elements_count)
223+
224+
225+
if __name__ == "__main__":
226+
__import__("doctest").testmod()

combinatorics/derangements.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from math import factorial
2+
3+
"""
4+
https://en.wikipedia.org/wiki/Derangement
5+
"""
6+
7+
8+
def derangement(elements_count: int) -> int:
9+
"""
10+
Returns the number of different combinations of selected_elements_count length which can
11+
be made from total_elements_count values, where total_elements_count >= selected_elements_count.
12+
13+
Examples:
14+
>>> derangement(6)
15+
265
16+
>>> derangement(7)
17+
1854
18+
>>> derangement(8)
19+
14833
20+
21+
>>> derangement(-5)
22+
Traceback (most recent call last):
23+
...
24+
ValueError: Please enter positive integers for elements_count
25+
"""
26+
if elements_count < 0:
27+
raise ValueError("Please enter positive integers for elements_count")
28+
29+
derangement = sum([((-1) ** i) / factorial(i) for i in range(elements_count + 1)])
30+
return int(factorial(elements_count) * derangement)
31+
32+
33+
if __name__ == "__main__":
34+
__import__("doctest").testmod()

combinatorics/stirling_second_kind.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
https://en.wikipedia.org/wiki/Stirling_numbers_of_the_second_kind
3+
"""
4+
5+
6+
def validate_elements_count(
7+
total_elements_count: int, selected_elements_count: int
8+
) -> None:
9+
# If either of the conditions are true, the function is being asked
10+
# to calculate a factorial of a negative number, which is not possible
11+
if total_elements_count < selected_elements_count or selected_elements_count < 0:
12+
raise ValueError(
13+
"Please enter positive integers for total_elements_count and selected_elements_count where total_elements_count >= selected_elements_count"
14+
)
15+
16+
17+
def stirling_second(total_elements_count: int, selected_elements_count: int) -> None:
18+
"""
19+
Returns the number of different combinations of selected_elements_count length which can
20+
be made from total_elements_count values, where total_elements_count >= selected_elements_count.
21+
22+
Examples:
23+
>>> stirling_second(6, 3)
24+
122
25+
>>> stirling_second(10, 5)
26+
50682
27+
>>> stirling_second(8, 3)
28+
1389
29+
>>> stirling_second(-5, 0)
30+
Traceback (most recent call last):
31+
...
32+
ValueError: Please enter positive integers for total_elements_count and selected_elements_count where total_elements_count >= selected_elements_count
33+
"""
34+
35+
validate_elements_count(total_elements_count, selected_elements_count)
36+
permutations_count = [
37+
[0] * (selected_elements_count + 1) for _ in range(total_elements_count + 1)
38+
]
39+
40+
for i in range(total_elements_count + 1):
41+
permutations_count[i][0] = 1
42+
43+
for i in range(1, total_elements_count + 1):
44+
for j in range(1, selected_elements_count + 1):
45+
permutations_count[i][j] = (
46+
j * permutations_count[i - 1][j] + permutations_count[i - 1][j - 1]
47+
)
48+
49+
return permutations_count[total_elements_count][selected_elements_count]
50+
51+
52+
if __name__ == "__main__":
53+
__import__("doctest").testmod()

0 commit comments

Comments
 (0)