Skip to content

Commit 196e658

Browse files
committed
2 parents 85f734d + c2bb3c4 commit 196e658

File tree

10 files changed

+172
-43
lines changed

10 files changed

+172
-43
lines changed

.pre-commit-config.yaml

+1-1
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.4.5
19+
rev: v0.4.7
2020
hooks:
2121
- id: ruff
2222
- id: ruff-format

DIRECTORY.md

-1
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,6 @@
661661
* [Manhattan Distance](maths/manhattan_distance.py)
662662
* [Matrix Exponentiation](maths/matrix_exponentiation.py)
663663
* [Max Sum Sliding Window](maths/max_sum_sliding_window.py)
664-
* [Median Of Two Arrays](maths/median_of_two_arrays.py)
665664
* [Minkowski Distance](maths/minkowski_distance.py)
666665
* [Mobius Function](maths/mobius_function.py)
667666
* [Modular Division](maths/modular_division.py)

backtracking/all_permutations.py

+36
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,42 @@ def create_state_space_tree(
2323
Creates a state space tree to iterate through each branch using DFS.
2424
We know that each state has exactly len(sequence) - index children.
2525
It terminates when it reaches the end of the given sequence.
26+
27+
:param sequence: The input sequence for which permutations are generated.
28+
:param current_sequence: The current permutation being built.
29+
:param index: The current index in the sequence.
30+
:param index_used: list to track which elements are used in permutation.
31+
32+
Example 1:
33+
>>> sequence = [1, 2, 3]
34+
>>> current_sequence = []
35+
>>> index_used = [False, False, False]
36+
>>> create_state_space_tree(sequence, current_sequence, 0, index_used)
37+
[1, 2, 3]
38+
[1, 3, 2]
39+
[2, 1, 3]
40+
[2, 3, 1]
41+
[3, 1, 2]
42+
[3, 2, 1]
43+
44+
Example 2:
45+
>>> sequence = ["A", "B", "C"]
46+
>>> current_sequence = []
47+
>>> index_used = [False, False, False]
48+
>>> create_state_space_tree(sequence, current_sequence, 0, index_used)
49+
['A', 'B', 'C']
50+
['A', 'C', 'B']
51+
['B', 'A', 'C']
52+
['B', 'C', 'A']
53+
['C', 'A', 'B']
54+
['C', 'B', 'A']
55+
56+
Example 3:
57+
>>> sequence = [1]
58+
>>> current_sequence = []
59+
>>> index_used = [False]
60+
>>> create_state_space_tree(sequence, current_sequence, 0, index_used)
61+
[1]
2662
"""
2763

2864
if index == len(sequence):

backtracking/all_subsequences.py

+51-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,56 @@ def create_state_space_tree(
2222
Creates a state space tree to iterate through each branch using DFS.
2323
We know that each state has exactly two children.
2424
It terminates when it reaches the end of the given sequence.
25+
26+
:param sequence: The input sequence for which subsequences are generated.
27+
:param current_subsequence: The current subsequence being built.
28+
:param index: The current index in the sequence.
29+
30+
Example:
31+
>>> sequence = [3, 2, 1]
32+
>>> current_subsequence = []
33+
>>> create_state_space_tree(sequence, current_subsequence, 0)
34+
[]
35+
[1]
36+
[2]
37+
[2, 1]
38+
[3]
39+
[3, 1]
40+
[3, 2]
41+
[3, 2, 1]
42+
43+
>>> sequence = ["A", "B"]
44+
>>> current_subsequence = []
45+
>>> create_state_space_tree(sequence, current_subsequence, 0)
46+
[]
47+
['B']
48+
['A']
49+
['A', 'B']
50+
51+
>>> sequence = []
52+
>>> current_subsequence = []
53+
>>> create_state_space_tree(sequence, current_subsequence, 0)
54+
[]
55+
56+
>>> sequence = [1, 2, 3, 4]
57+
>>> current_subsequence = []
58+
>>> create_state_space_tree(sequence, current_subsequence, 0)
59+
[]
60+
[4]
61+
[3]
62+
[3, 4]
63+
[2]
64+
[2, 4]
65+
[2, 3]
66+
[2, 3, 4]
67+
[1]
68+
[1, 4]
69+
[1, 3]
70+
[1, 3, 4]
71+
[1, 2]
72+
[1, 2, 4]
73+
[1, 2, 3]
74+
[1, 2, 3, 4]
2575
"""
2676

2777
if index == len(sequence):
@@ -35,7 +85,7 @@ def create_state_space_tree(
3585

3686

3787
if __name__ == "__main__":
38-
seq: list[Any] = [3, 1, 2, 4]
88+
seq: list[Any] = [1, 2, 3]
3989
generate_all_subsequences(seq)
4090

4191
seq.clear()

bit_manipulation/binary_and_operator.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def binary_and(a: int, b: int) -> str:
2626
>>> binary_and(0, 1.1)
2727
Traceback (most recent call last):
2828
...
29-
TypeError: 'float' object cannot be interpreted as an integer
29+
ValueError: Unknown format code 'b' for object of type 'float'
3030
>>> binary_and("0", "1")
3131
Traceback (most recent call last):
3232
...
@@ -35,8 +35,8 @@ def binary_and(a: int, b: int) -> str:
3535
if a < 0 or b < 0:
3636
raise ValueError("the value of both inputs must be positive")
3737

38-
a_binary = str(bin(a))[2:] # remove the leading "0b"
39-
b_binary = str(bin(b))[2:] # remove the leading "0b"
38+
a_binary = format(a, "b")
39+
b_binary = format(b, "b")
4040

4141
max_len = max(len(a_binary), len(b_binary))
4242

divide_and_conquer/power.py

+18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@ def actual_power(a: int, b: int):
22
"""
33
Function using divide and conquer to calculate a^b.
44
It only works for integer a,b.
5+
6+
:param a: The base of the power operation, an integer.
7+
:param b: The exponent of the power operation, a non-negative integer.
8+
:return: The result of a^b.
9+
10+
Examples:
11+
>>> actual_power(3, 2)
12+
9
13+
>>> actual_power(5, 3)
14+
125
15+
>>> actual_power(2, 5)
16+
32
17+
>>> actual_power(7, 0)
18+
1
519
"""
620
if b == 0:
721
return 1
@@ -13,6 +27,10 @@ def actual_power(a: int, b: int):
1327

1428
def power(a: int, b: int) -> float:
1529
"""
30+
:param a: The base (integer).
31+
:param b: The exponent (integer).
32+
:return: The result of a^b, as a float for negative exponents.
33+
1634
>>> power(4,6)
1735
4096
1836
>>> power(2,3)

machine_learning/loss_functions.py

+34
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,40 @@ def smooth_l1_loss(y_true: np.ndarray, y_pred: np.ndarray, beta: float = 1.0) ->
629629
return np.mean(loss)
630630

631631

632+
def kullback_leibler_divergence(y_true: np.ndarray, y_pred: np.ndarray) -> float:
633+
"""
634+
Calculate the Kullback-Leibler divergence (KL divergence) loss between true labels
635+
and predicted probabilities.
636+
637+
KL divergence loss quantifies dissimilarity between true labels and predicted
638+
probabilities. It's often used in training generative models.
639+
640+
KL = Σ(y_true * ln(y_true / y_pred))
641+
642+
Reference: https://en.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence
643+
644+
Parameters:
645+
- y_true: True class probabilities
646+
- y_pred: Predicted class probabilities
647+
648+
>>> true_labels = np.array([0.2, 0.3, 0.5])
649+
>>> predicted_probs = np.array([0.3, 0.3, 0.4])
650+
>>> kullback_leibler_divergence(true_labels, predicted_probs)
651+
0.030478754035472025
652+
>>> true_labels = np.array([0.2, 0.3, 0.5])
653+
>>> predicted_probs = np.array([0.3, 0.3, 0.4, 0.5])
654+
>>> kullback_leibler_divergence(true_labels, predicted_probs)
655+
Traceback (most recent call last):
656+
...
657+
ValueError: Input arrays must have the same length.
658+
"""
659+
if len(y_true) != len(y_pred):
660+
raise ValueError("Input arrays must have the same length.")
661+
662+
kl_loss = y_true * np.log(y_true / y_pred)
663+
return np.sum(kl_loss)
664+
665+
632666
if __name__ == "__main__":
633667
import doctest
634668

machine_learning/sequential_minimum_optimization.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,11 @@ def test_cancel_data():
463463
with open(r"cancel_data.csv", "w") as f:
464464
f.write(content)
465465

466-
data = pd.read_csv(r"cancel_data.csv", header=None)
466+
data = pd.read_csv(
467+
"cancel_data.csv",
468+
header=None,
469+
dtype={0: str}, # Assuming the first column contains string data
470+
)
467471

468472
# 1: pre-processing data
469473
del data[data.columns.tolist()[0]]

maths/median_of_two_arrays.py

-33
This file was deleted.

searches/binary_search.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from bisect import bisect_left, bisect_right
44

5+
56
def bisect_left_custom(sorted_collection, item, lo=0, hi=None):
67
"""
78
Custom implementation of bisect_left.
@@ -17,6 +18,7 @@ def bisect_left_custom(sorted_collection, item, lo=0, hi=None):
1718
hi = mid
1819
return lo
1920

21+
2022
def bisect_right_custom(sorted_collection, item, lo=0, hi=None):
2123
"""
2224
Custom implementation of bisect_right.
@@ -32,18 +34,21 @@ def bisect_right_custom(sorted_collection, item, lo=0, hi=None):
3234
hi = mid
3335
return lo
3436

37+
3538
def insort_left_custom(sorted_collection, item, lo=0, hi=None):
3639
"""
3740
Inserts item into sorted_collection in sorted order (using bisect_left_custom).
3841
"""
3942
sorted_collection.insert(bisect_left_custom(sorted_collection, item, lo, hi), item)
4043

44+
4145
def insort_right_custom(sorted_collection, item, lo=0, hi=None):
4246
"""
4347
Inserts item into sorted_collection in sorted order (using bisect_right_custom).
4448
"""
4549
sorted_collection.insert(bisect_right_custom(sorted_collection, item, lo, hi), item)
4650

51+
4752
def binary_search(sorted_collection, item):
4853
"""
4954
Standard binary search implementation.
@@ -60,6 +65,7 @@ def binary_search(sorted_collection, item):
6065
hi = mid - 1
6166
return -1
6267

68+
6369
def binary_search_std_lib(sorted_collection, item):
6470
"""
6571
Binary search using Python's standard library bisect module.
@@ -69,6 +75,7 @@ def binary_search_std_lib(sorted_collection, item):
6975
return index
7076
return -1
7177

78+
7279
def binary_search_by_recursion(sorted_collection, item, lo=0, hi=None):
7380
"""
7481
Binary search using recursion.
@@ -85,6 +92,7 @@ def binary_search_by_recursion(sorted_collection, item, lo=0, hi=None):
8592
else:
8693
return binary_search_by_recursion(sorted_collection, item, mid + 1, hi)
8794

95+
8896
def exponential_search(sorted_collection, item):
8997
"""
9098
Exponential search implementation.
@@ -95,7 +103,10 @@ def exponential_search(sorted_collection, item):
95103
bound = 1
96104
while bound < len(sorted_collection) and sorted_collection[bound] < item:
97105
bound *= 2
98-
return binary_search_by_recursion(sorted_collection, item, bound // 2, min(bound, len(sorted_collection) - 1))
106+
return binary_search_by_recursion(
107+
sorted_collection, item, bound // 2, min(bound, len(sorted_collection) - 1)
108+
)
109+
99110

100111
if __name__ == "__main__":
101112
import doctest
@@ -105,7 +116,12 @@ def exponential_search(sorted_collection, item):
105116
doctest.testmod()
106117

107118
# List of search functions to benchmark
108-
searches = [binary_search_std_lib, binary_search, exponential_search, binary_search_by_recursion]
119+
searches = [
120+
binary_search_std_lib,
121+
binary_search,
122+
exponential_search,
123+
binary_search_by_recursion,
124+
]
109125

110126
# Test and print results of searching for 10 in a sample list
111127
for search in searches:
@@ -115,7 +131,12 @@ def exponential_search(sorted_collection, item):
115131
setup = "collection = list(range(1000))"
116132
# Benchmark each search function
117133
for search in searches:
118-
time = timeit.timeit(f"{search.__name__}(collection, 500)", setup=setup, number=5000, globals=globals())
134+
time = timeit.timeit(
135+
f"{search.__name__}(collection, 500)",
136+
setup=setup,
137+
number=5000,
138+
globals=globals(),
139+
)
119140
print(f"{search.__name__:>26}: {time:.6f}")
120141

121142
# Interactive part: user inputs a list and a target number

0 commit comments

Comments
 (0)