Skip to content

Commit 58273e7

Browse files
Merge branch 'TheAlgorithms:master' into physics
2 parents f33964a + 47a44ab commit 58273e7

7 files changed

Lines changed: 360 additions & 10 deletions

File tree

boolean_algebra/and_gate.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
"""
2-
An AND Gate is a logic gate in boolean algebra which results to 1 (True) if both the
3-
inputs are 1, and 0 (False) otherwise.
2+
An AND Gate is a logic gate in boolean algebra which results to 1 (True) if all the
3+
inputs are 1 (True), and 0 (False) otherwise.
44
5-
Following is the truth table of an AND Gate:
5+
Following is the truth table of a Two Input AND Gate:
66
------------------------------
77
| Input 1 | Input 2 | Output |
88
------------------------------
@@ -12,7 +12,7 @@
1212
| 1 | 1 | 1 |
1313
------------------------------
1414
15-
Refer - https://www.geeksforgeeks.org/logic-gates-in-python/
15+
Refer - https://www.geeksforgeeks.org/logic-gates/
1616
"""
1717

1818

@@ -32,6 +32,18 @@ def and_gate(input_1: int, input_2: int) -> int:
3232
return int(input_1 and input_2)
3333

3434

35+
def n_input_and_gate(inputs: list[int]) -> int:
36+
"""
37+
Calculate AND of a list of input values
38+
39+
>>> n_input_and_gate([1, 0, 1, 1, 0])
40+
0
41+
>>> n_input_and_gate([1, 1, 1, 1, 1])
42+
1
43+
"""
44+
return int(all(inputs))
45+
46+
3547
if __name__ == "__main__":
3648
import doctest
3749

data_structures/binary_tree/lowest_common_ancestor.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ def swap(a: int, b: int) -> tuple[int, int]:
1515
(4, 3)
1616
>>> swap(67, 12)
1717
(12, 67)
18+
>>> swap(3,-4)
19+
(-4, 3)
1820
"""
1921
a ^= b
2022
b ^= a
@@ -25,6 +27,23 @@ def swap(a: int, b: int) -> tuple[int, int]:
2527
def create_sparse(max_node: int, parent: list[list[int]]) -> list[list[int]]:
2628
"""
2729
creating sparse table which saves each nodes 2^i-th parent
30+
>>> max_node = 6
31+
>>> parent = [[0, 0, 1, 1, 2, 2, 3]] + [[0] * 7 for _ in range(19)]
32+
>>> parent = create_sparse(max_node=max_node, parent=parent)
33+
>>> parent[0]
34+
[0, 0, 1, 1, 2, 2, 3]
35+
>>> parent[1]
36+
[0, 0, 0, 0, 1, 1, 1]
37+
>>> parent[2]
38+
[0, 0, 0, 0, 0, 0, 0]
39+
40+
>>> max_node = 1
41+
>>> parent = [[0, 0]] + [[0] * 2 for _ in range(19)]
42+
>>> parent = create_sparse(max_node=max_node, parent=parent)
43+
>>> parent[0]
44+
[0, 0]
45+
>>> parent[1]
46+
[0, 0]
2847
"""
2948
j = 1
3049
while (1 << j) < max_node:
@@ -38,6 +57,21 @@ def create_sparse(max_node: int, parent: list[list[int]]) -> list[list[int]]:
3857
def lowest_common_ancestor(
3958
u: int, v: int, level: list[int], parent: list[list[int]]
4059
) -> int:
60+
"""
61+
Return the lowest common ancestor between u and v
62+
63+
>>> level = [-1, 0, 1, 1, 2, 2, 2]
64+
>>> parent = [[0, 0, 1, 1, 2, 2, 3],[0, 0, 0, 0, 1, 1, 1]] + \
65+
[[0] * 7 for _ in range(17)]
66+
>>> lowest_common_ancestor(u=4, v=5, level=level, parent=parent)
67+
2
68+
>>> lowest_common_ancestor(u=4, v=6, level=level, parent=parent)
69+
1
70+
>>> lowest_common_ancestor(u=2, v=3, level=level, parent=parent)
71+
1
72+
>>> lowest_common_ancestor(u=6, v=6, level=level, parent=parent)
73+
6
74+
"""
4175
# u must be deeper in the tree than v
4276
if level[u] < level[v]:
4377
u, v = swap(u, v)
@@ -68,6 +102,26 @@ def breadth_first_search(
68102
sets every nodes direct parent
69103
parent of root node is set to 0
70104
calculates depth of each node from root node
105+
>>> level = [-1] * 7
106+
>>> parent = [[0] * 7 for _ in range(20)]
107+
>>> graph = {1: [2, 3], 2: [4, 5], 3: [6], 4: [], 5: [], 6: []}
108+
>>> level, parent = breadth_first_search(
109+
... level=level, parent=parent, max_node=6, graph=graph, root=1)
110+
>>> level
111+
[-1, 0, 1, 1, 2, 2, 2]
112+
>>> parent[0]
113+
[0, 0, 1, 1, 2, 2, 3]
114+
115+
116+
>>> level = [-1] * 2
117+
>>> parent = [[0] * 2 for _ in range(20)]
118+
>>> graph = {1: []}
119+
>>> level, parent = breadth_first_search(
120+
... level=level, parent=parent, max_node=1, graph=graph, root=1)
121+
>>> level
122+
[-1, 0]
123+
>>> parent[0]
124+
[0, 0]
71125
"""
72126
level[root] = 0
73127
q: Queue[int] = Queue(maxsize=max_node)

dynamic_programming/longest_common_substring.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,25 @@ def longest_common_substring(text1: str, text2: str) -> str:
4343
if not (isinstance(text1, str) and isinstance(text2, str)):
4444
raise ValueError("longest_common_substring() takes two strings for inputs")
4545

46+
if not text1 or not text2:
47+
return ""
48+
4649
text1_length = len(text1)
4750
text2_length = len(text2)
4851

4952
dp = [[0] * (text2_length + 1) for _ in range(text1_length + 1)]
50-
ans_index = 0
51-
ans_length = 0
53+
end_pos = 0
54+
max_length = 0
5255

5356
for i in range(1, text1_length + 1):
5457
for j in range(1, text2_length + 1):
5558
if text1[i - 1] == text2[j - 1]:
5659
dp[i][j] = 1 + dp[i - 1][j - 1]
57-
if dp[i][j] > ans_length:
58-
ans_index = i
59-
ans_length = dp[i][j]
60+
if dp[i][j] > max_length:
61+
end_pos = i
62+
max_length = dp[i][j]
6063

61-
return text1[ans_index - ans_length : ans_index]
64+
return text1[end_pos - max_length : end_pos]
6265

6366

6467
if __name__ == "__main__":

project_euler/problem_095/__init__.py

Whitespace-only changes.

project_euler/problem_095/sol1.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"""
2+
Project Euler Problem 95: https://projecteuler.net/problem=95
3+
4+
Amicable Chains
5+
6+
The proper divisors of a number are all the divisors excluding the number itself.
7+
For example, the proper divisors of 28 are 1, 2, 4, 7, and 14.
8+
As the sum of these divisors is equal to 28, we call it a perfect number.
9+
10+
Interestingly the sum of the proper divisors of 220 is 284 and
11+
the sum of the proper divisors of 284 is 220, forming a chain of two numbers.
12+
For this reason, 220 and 284 are called an amicable pair.
13+
14+
Perhaps less well known are longer chains.
15+
For example, starting with 12496, we form a chain of five numbers:
16+
12496 -> 14288 -> 15472 -> 14536 -> 14264 (-> 12496 -> ...)
17+
18+
Since this chain returns to its starting point, it is called an amicable chain.
19+
20+
Find the smallest member of the longest amicable chain with
21+
no element exceeding one million.
22+
23+
Solution is doing the following:
24+
- Get relevant prime numbers
25+
- Iterate over product combination of prime numbers to generate all non-prime
26+
numbers up to max number, by keeping track of prime factors
27+
- Calculate the sum of factors for each number
28+
- Iterate over found some factors to find longest chain
29+
"""
30+
31+
from math import isqrt
32+
33+
34+
def generate_primes(max_num: int) -> list[int]:
35+
"""
36+
Calculates the list of primes up to and including `max_num`.
37+
38+
>>> generate_primes(6)
39+
[2, 3, 5]
40+
"""
41+
are_primes = [True] * (max_num + 1)
42+
are_primes[0] = are_primes[1] = False
43+
for i in range(2, isqrt(max_num) + 1):
44+
if are_primes[i]:
45+
for j in range(i * i, max_num + 1, i):
46+
are_primes[j] = False
47+
48+
return [prime for prime, is_prime in enumerate(are_primes) if is_prime]
49+
50+
51+
def multiply(
52+
chain: list[int],
53+
primes: list[int],
54+
min_prime_idx: int,
55+
prev_num: int,
56+
max_num: int,
57+
prev_sum: int,
58+
primes_degrees: dict[int, int],
59+
) -> None:
60+
"""
61+
Run over all prime combinations to generate non-prime numbers.
62+
63+
>>> chain = [0] * 3
64+
>>> primes_degrees = {}
65+
>>> multiply(
66+
... chain=chain,
67+
... primes=[2],
68+
... min_prime_idx=0,
69+
... prev_num=1,
70+
... max_num=2,
71+
... prev_sum=0,
72+
... primes_degrees=primes_degrees,
73+
... )
74+
>>> chain
75+
[0, 0, 1]
76+
>>> primes_degrees
77+
{2: 1}
78+
"""
79+
80+
min_prime = primes[min_prime_idx]
81+
num = prev_num * min_prime
82+
83+
min_prime_degree = primes_degrees.get(min_prime, 0)
84+
min_prime_degree += 1
85+
primes_degrees[min_prime] = min_prime_degree
86+
87+
new_sum = prev_sum * min_prime + (prev_sum + prev_num) * (min_prime - 1) // (
88+
min_prime**min_prime_degree - 1
89+
)
90+
chain[num] = new_sum
91+
92+
for prime_idx in range(min_prime_idx, len(primes)):
93+
if primes[prime_idx] * num > max_num:
94+
break
95+
96+
multiply(
97+
chain=chain,
98+
primes=primes,
99+
min_prime_idx=prime_idx,
100+
prev_num=num,
101+
max_num=max_num,
102+
prev_sum=new_sum,
103+
primes_degrees=primes_degrees.copy(),
104+
)
105+
106+
107+
def find_longest_chain(chain: list[int], max_num: int) -> int:
108+
"""
109+
Finds the smallest element of longest chain
110+
111+
>>> find_longest_chain(chain=[0, 0, 0, 0, 0, 0, 6], max_num=6)
112+
6
113+
"""
114+
115+
max_len = 0
116+
min_elem = 0
117+
for start in range(2, len(chain)):
118+
visited = {start}
119+
elem = chain[start]
120+
length = 1
121+
122+
while elem > 1 and elem <= max_num and elem not in visited:
123+
visited.add(elem)
124+
elem = chain[elem]
125+
length += 1
126+
127+
if elem == start and length > max_len:
128+
max_len = length
129+
min_elem = start
130+
131+
return min_elem
132+
133+
134+
def solution(max_num: int = 1000000) -> int:
135+
"""
136+
Runs the calculation for numbers <= `max_num`.
137+
138+
>>> solution(10)
139+
6
140+
>>> solution(200000)
141+
12496
142+
"""
143+
144+
primes = generate_primes(max_num)
145+
chain = [0] * (max_num + 1)
146+
for prime_idx, prime in enumerate(primes):
147+
if prime**2 > max_num:
148+
break
149+
150+
multiply(
151+
chain=chain,
152+
primes=primes,
153+
min_prime_idx=prime_idx,
154+
prev_num=1,
155+
max_num=max_num,
156+
prev_sum=0,
157+
primes_degrees={},
158+
)
159+
160+
return find_longest_chain(chain=chain, max_num=max_num)
161+
162+
163+
if __name__ == "__main__":
164+
print(f"{solution() = }")

project_euler/problem_345/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)