Skip to content

Commit cacd998

Browse files
authored
Merge branch 'TheAlgorithms:master' into master
2 parents a345fae + 0040ad4 commit cacd998

31 files changed

+256
-102
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ repos:
1616
- id: auto-walrus
1717

1818
- repo: https://github.com/astral-sh/ruff-pre-commit
19-
rev: v0.8.6
19+
rev: v0.9.1
2020
hooks:
2121
- id: ruff
2222
- id: ruff-format

DIRECTORY.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
* [Baconian Cipher](ciphers/baconian_cipher.py)
8787
* [Base16](ciphers/base16.py)
8888
* [Base32](ciphers/base32.py)
89-
* [Base64](ciphers/base64.py)
89+
* [Base64 Cipher](ciphers/base64_cipher.py)
9090
* [Base85](ciphers/base85.py)
9191
* [Beaufort Cipher](ciphers/beaufort_cipher.py)
9292
* [Bifid](ciphers/bifid.py)
@@ -1331,7 +1331,7 @@
13311331
* [Title](strings/title.py)
13321332
* [Top K Frequent Words](strings/top_k_frequent_words.py)
13331333
* [Upper](strings/upper.py)
1334-
* [Wave](strings/wave.py)
1334+
* [Wave String](strings/wave_string.py)
13351335
* [Wildcard Pattern Matching](strings/wildcard_pattern_matching.py)
13361336
* [Word Occurrence](strings/word_occurrence.py)
13371337
* [Word Patterns](strings/word_patterns.py)

backtracking/all_combinations.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
def combination_lists(n: int, k: int) -> list[list[int]]:
1414
"""
15+
Generates all possible combinations of k numbers out of 1 ... n using itertools.
16+
1517
>>> combination_lists(n=4, k=2)
1618
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
1719
"""
@@ -20,6 +22,8 @@ def combination_lists(n: int, k: int) -> list[list[int]]:
2022

2123
def generate_all_combinations(n: int, k: int) -> list[list[int]]:
2224
"""
25+
Generates all possible combinations of k numbers out of 1 ... n using backtracking.
26+
2327
>>> generate_all_combinations(n=4, k=2)
2428
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
2529
>>> generate_all_combinations(n=0, k=0)
@@ -34,6 +38,14 @@ def generate_all_combinations(n: int, k: int) -> list[list[int]]:
3438
ValueError: n must not be negative
3539
>>> generate_all_combinations(n=5, k=4)
3640
[[1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]]
41+
>>> generate_all_combinations(n=3, k=3)
42+
[[1, 2, 3]]
43+
>>> generate_all_combinations(n=3, k=1)
44+
[[1], [2], [3]]
45+
>>> generate_all_combinations(n=1, k=0)
46+
[[]]
47+
>>> generate_all_combinations(n=1, k=1)
48+
[[1]]
3749
>>> from itertools import combinations
3850
>>> all(generate_all_combinations(n, k) == combination_lists(n, k)
3951
... for n in range(1, 6) for k in range(1, 6))
@@ -56,6 +68,28 @@ def create_all_state(
5668
current_list: list[int],
5769
total_list: list[list[int]],
5870
) -> None:
71+
"""
72+
Helper function to recursively build all combinations.
73+
74+
>>> create_all_state(1, 4, 2, [], result := [])
75+
>>> result
76+
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
77+
>>> create_all_state(1, 3, 3, [], result := [])
78+
>>> result
79+
[[1, 2, 3]]
80+
>>> create_all_state(2, 2, 1, [1], result := [])
81+
>>> result
82+
[[1, 2]]
83+
>>> create_all_state(1, 0, 0, [], result := [])
84+
>>> result
85+
[[]]
86+
>>> create_all_state(1, 4, 0, [1, 2], result := [])
87+
>>> result
88+
[[1, 2]]
89+
>>> create_all_state(5, 4, 2, [1, 2], result := [])
90+
>>> result
91+
[]
92+
"""
5993
if level == 0:
6094
total_list.append(current_list[:])
6195
return

ciphers/base64.py renamed to ciphers/base64_cipher.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,13 @@ def base64_decode(encoded_data: str) -> bytes:
105105

106106
# Check if the encoded string contains non base64 characters
107107
if padding:
108-
assert all(
109-
char in B64_CHARSET for char in encoded_data[:-padding]
110-
), "Invalid base64 character(s) found."
108+
assert all(char in B64_CHARSET for char in encoded_data[:-padding]), (
109+
"Invalid base64 character(s) found."
110+
)
111111
else:
112-
assert all(
113-
char in B64_CHARSET for char in encoded_data
114-
), "Invalid base64 character(s) found."
112+
assert all(char in B64_CHARSET for char in encoded_data), (
113+
"Invalid base64 character(s) found."
114+
)
115115

116116
# Check the padding
117117
assert len(encoded_data) % 4 == 0 and padding < 3, "Incorrect padding"

ciphers/caesar_cipher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def brute_force(input_string: str, alphabet: str | None = None) -> dict[int, str
225225

226226
if __name__ == "__main__":
227227
while True:
228-
print(f'\n{"-" * 10}\n Menu\n{"-" * 10}')
228+
print(f"\n{'-' * 10}\n Menu\n{'-' * 10}")
229229
print(*["1.Encrypt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n")
230230

231231
# get user input

computer_vision/flip_augmentation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def main() -> None:
3333
file_name = paths[index].split(os.sep)[-1].rsplit(".", 1)[0]
3434
file_root = f"{OUTPUT_DIR}/{file_name}_FLIP_{letter_code}"
3535
cv2.imwrite(f"{file_root}.jpg", image, [cv2.IMWRITE_JPEG_QUALITY, 85])
36-
print(f"Success {index+1}/{len(new_images)} with {file_name}")
36+
print(f"Success {index + 1}/{len(new_images)} with {file_name}")
3737
annos_list = []
3838
for anno in new_annos[index]:
3939
obj = f"{anno[0]} {anno[1]} {anno[2]} {anno[3]} {anno[4]}"

computer_vision/mosaic_augmentation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def main() -> None:
4141
file_name = path.split(os.sep)[-1].rsplit(".", 1)[0]
4242
file_root = f"{OUTPUT_DIR}/{file_name}_MOSAIC_{letter_code}"
4343
cv2.imwrite(f"{file_root}.jpg", new_image, [cv2.IMWRITE_JPEG_QUALITY, 85])
44-
print(f"Succeeded {index+1}/{NUMBER_IMAGES} with {file_name}")
44+
print(f"Succeeded {index + 1}/{NUMBER_IMAGES} with {file_name}")
4545
annos_list = []
4646
for anno in new_annos:
4747
width = anno[3] - anno[1]

data_structures/hashing/number_theory/prime_numbers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ def is_prime(number: int) -> bool:
3232
"""
3333

3434
# precondition
35-
assert isinstance(number, int) and (
36-
number >= 0
37-
), "'number' must been an int and positive"
35+
assert isinstance(number, int) and (number >= 0), (
36+
"'number' must been an int and positive"
37+
)
3838

3939
if 1 < number < 4:
4040
# 2 and 3 are primes

data_structures/heap/min_heap.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ def is_empty(self):
124124
return len(self.heap) == 0
125125

126126
def decrease_key(self, node, new_value):
127-
assert (
128-
self.heap[self.idx_of_element[node]].val > new_value
129-
), "newValue must be less that current value"
127+
assert self.heap[self.idx_of_element[node]].val > new_value, (
128+
"newValue must be less that current value"
129+
)
130130
node.val = new_value
131131
self.heap_dict[node.name] = new_value
132132
self.sift_up(self.idx_of_element[node])

data_structures/kd_tree/tests/test_kdtree.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ def test_build_kdtree(num_points, cube_size, num_dimensions, depth, expected_res
4848
assert kdtree is not None, "Expected a KDNode, got None"
4949

5050
# Check if root has correct dimensions
51-
assert (
52-
len(kdtree.point) == num_dimensions
53-
), f"Expected point dimension {num_dimensions}, got {len(kdtree.point)}"
51+
assert len(kdtree.point) == num_dimensions, (
52+
f"Expected point dimension {num_dimensions}, got {len(kdtree.point)}"
53+
)
5454

5555
# Check that the tree is balanced to some extent (simplistic check)
56-
assert isinstance(
57-
kdtree, KDNode
58-
), f"Expected KDNode instance, got {type(kdtree)}"
56+
assert isinstance(kdtree, KDNode), (
57+
f"Expected KDNode instance, got {type(kdtree)}"
58+
)
5959

6060

6161
def test_nearest_neighbour_search():

data_structures/suffix_tree/tests/test_suffix_tree.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,37 +22,37 @@ def test_search_existing_patterns(self) -> None:
2222
patterns = ["ana", "ban", "na"]
2323
for pattern in patterns:
2424
with self.subTest(pattern=pattern):
25-
assert self.suffix_tree.search(
26-
pattern
27-
), f"Pattern '{pattern}' should be found."
25+
assert self.suffix_tree.search(pattern), (
26+
f"Pattern '{pattern}' should be found."
27+
)
2828

2929
def test_search_non_existing_patterns(self) -> None:
3030
"""Test searching for patterns that do not exist in the suffix tree."""
3131
patterns = ["xyz", "apple", "cat"]
3232
for pattern in patterns:
3333
with self.subTest(pattern=pattern):
34-
assert not self.suffix_tree.search(
35-
pattern
36-
), f"Pattern '{pattern}' should not be found."
34+
assert not self.suffix_tree.search(pattern), (
35+
f"Pattern '{pattern}' should not be found."
36+
)
3737

3838
def test_search_empty_pattern(self) -> None:
3939
"""Test searching for an empty pattern."""
4040
assert self.suffix_tree.search(""), "An empty pattern should be found."
4141

4242
def test_search_full_text(self) -> None:
4343
"""Test searching for the full text."""
44-
assert self.suffix_tree.search(
45-
self.text
46-
), "The full text should be found in the suffix tree."
44+
assert self.suffix_tree.search(self.text), (
45+
"The full text should be found in the suffix tree."
46+
)
4747

4848
def test_search_substrings(self) -> None:
4949
"""Test searching for substrings of the full text."""
5050
substrings = ["ban", "ana", "a", "na"]
5151
for substring in substrings:
5252
with self.subTest(substring=substring):
53-
assert self.suffix_tree.search(
54-
substring
55-
), f"Substring '{substring}' should be found."
53+
assert self.suffix_tree.search(substring), (
54+
f"Substring '{substring}' should be found."
55+
)
5656

5757

5858
if __name__ == "__main__":

dynamic_programming/climbing_stairs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ def climb_stairs(number_of_steps: int) -> int:
2525
...
2626
AssertionError: number_of_steps needs to be positive integer, your input -7
2727
"""
28-
assert (
29-
isinstance(number_of_steps, int) and number_of_steps > 0
30-
), f"number_of_steps needs to be positive integer, your input {number_of_steps}"
28+
assert isinstance(number_of_steps, int) and number_of_steps > 0, (
29+
f"number_of_steps needs to be positive integer, your input {number_of_steps}"
30+
)
3131
if number_of_steps == 1:
3232
return 1
3333
previous, current = 1, 1

dynamic_programming/iterating_through_submasks.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ def list_of_submasks(mask: int) -> list[int]:
3737
3838
"""
3939

40-
assert (
41-
isinstance(mask, int) and mask > 0
42-
), f"mask needs to be positive integer, your input {mask}"
40+
assert isinstance(mask, int) and mask > 0, (
41+
f"mask needs to be positive integer, your input {mask}"
42+
)
4343

4444
"""
4545
first submask iterated will be mask itself then operation will be performed

dynamic_programming/longest_increasing_subsequence.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ def longest_subsequence(array: list[int]) -> list[int]: # This function is recu
2424
[10, 22, 33, 41, 60, 80]
2525
>>> longest_subsequence([4, 8, 7, 5, 1, 12, 2, 3, 9])
2626
[1, 2, 3, 9]
27+
>>> longest_subsequence([28, 26, 12, 23, 35, 39])
28+
[12, 23, 35, 39]
2729
>>> longest_subsequence([9, 8, 7, 6, 5, 7])
28-
[8]
30+
[5, 7]
2931
>>> longest_subsequence([1, 1, 1])
3032
[1, 1, 1]
3133
>>> longest_subsequence([])
@@ -44,7 +46,7 @@ def longest_subsequence(array: list[int]) -> list[int]: # This function is recu
4446
while not is_found and i < array_length:
4547
if array[i] < pivot:
4648
is_found = True
47-
temp_array = [element for element in array[i:] if element >= array[i]]
49+
temp_array = array[i:]
4850
temp_array = longest_subsequence(temp_array)
4951
if len(temp_array) > len(longest_subseq):
5052
longest_subseq = temp_array
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""
2+
Author : Sanjay Muthu <https://github.com/XenoBytesX>
3+
4+
This is a pure Python implementation of Dynamic Programming solution to the longest
5+
increasing subsequence of a given sequence.
6+
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, 50, 60, 80]`` as output
14+
"""
15+
16+
from __future__ import annotations
17+
18+
import copy
19+
20+
21+
def longest_subsequence(array: list[int]) -> list[int]:
22+
"""
23+
Some examples
24+
25+
>>> longest_subsequence([10, 22, 9, 33, 21, 50, 41, 60, 80])
26+
[10, 22, 33, 50, 60, 80]
27+
>>> longest_subsequence([4, 8, 7, 5, 1, 12, 2, 3, 9])
28+
[1, 2, 3, 9]
29+
>>> longest_subsequence([9, 8, 7, 6, 5, 7])
30+
[7, 7]
31+
>>> longest_subsequence([28, 26, 12, 23, 35, 39])
32+
[12, 23, 35, 39]
33+
>>> longest_subsequence([1, 1, 1])
34+
[1, 1, 1]
35+
>>> longest_subsequence([])
36+
[]
37+
"""
38+
n = len(array)
39+
# The longest increasing subsequence ending at array[i]
40+
longest_increasing_subsequence = []
41+
for i in range(n):
42+
longest_increasing_subsequence.append([array[i]])
43+
44+
for i in range(1, n):
45+
for prev in range(i):
46+
# If array[prev] is less than or equal to array[i], then
47+
# longest_increasing_subsequence[prev] + array[i]
48+
# is a valid increasing subsequence
49+
50+
# longest_increasing_subsequence[i] is only set to
51+
# longest_increasing_subsequence[prev] + array[i] if the length is longer.
52+
53+
if array[prev] <= array[i] and len(
54+
longest_increasing_subsequence[prev]
55+
) + 1 > len(longest_increasing_subsequence[i]):
56+
longest_increasing_subsequence[i] = copy.copy(
57+
longest_increasing_subsequence[prev]
58+
)
59+
longest_increasing_subsequence[i].append(array[i])
60+
61+
result: list[int] = []
62+
for i in range(n):
63+
if len(longest_increasing_subsequence[i]) > len(result):
64+
result = longest_increasing_subsequence[i]
65+
66+
return result
67+
68+
69+
if __name__ == "__main__":
70+
import doctest
71+
72+
doctest.testmod()

dynamic_programming/matrix_chain_multiplication.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def elapsed_time(msg: str) -> Iterator:
134134

135135
start = perf_counter_ns()
136136
yield
137-
print(f"Finished: {msg} in {(perf_counter_ns() - start) / 10 ** 9} seconds.")
137+
print(f"Finished: {msg} in {(perf_counter_ns() - start) / 10**9} seconds.")
138138

139139

140140
if __name__ == "__main__":

0 commit comments

Comments
 (0)