Skip to content

Commit ed27f27

Browse files
authored
Merge branch 'master' into bitonic-sort-comparable
2 parents 568ac1a + d961217 commit ed27f27

16 files changed

Lines changed: 868 additions & 561 deletions

‎DIRECTORY.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@
481481
* [Longest Increasing Subsequence Iterative](dynamic_programming/longest_increasing_subsequence_iterative.py)
482482
* [Longest Increasing Subsequence O Nlogn](dynamic_programming/longest_increasing_subsequence_o_nlogn.py)
483483
* [Longest Palindromic Subsequence](dynamic_programming/longest_palindromic_subsequence.py)
484+
* [Longest Repeating Subsequence](dynamic_programming/longest_repeating_subsequence.py)
484485
* [Matrix Chain Multiplication](dynamic_programming/matrix_chain_multiplication.py)
485486
* [Matrix Chain Order](dynamic_programming/matrix_chain_order.py)
486487
* [Max Non Adjacent Sum](dynamic_programming/max_non_adjacent_sum.py)
@@ -702,6 +703,7 @@
702703
* [Enigma Machine](hashes/enigma_machine.py)
703704
* [Fletcher16](hashes/fletcher16.py)
704705
* [Hamming Code](hashes/hamming_code.py)
706+
* [Jenkins One At A Time](hashes/jenkins_one_at_a_time.py)
705707
* [Luhn](hashes/luhn.py)
706708
* [Md5](hashes/md5.py)
707709
* [Sdbm](hashes/sdbm.py)
@@ -1026,6 +1028,7 @@
10261028
* [Rotate Matrix](matrix/rotate_matrix.py)
10271029
* [Searching In Sorted Matrix](matrix/searching_in_sorted_matrix.py)
10281030
* [Sherman Morrison](matrix/sherman_morrison.py)
1031+
* [Similar Matrices](matrix/similar_matrices.py)
10291032
* [Spiral Print](matrix/spiral_print.py)
10301033
* Tests
10311034
* [Test Matrix Operation](matrix/tests/test_matrix_operation.py)
@@ -1054,6 +1057,7 @@
10541057
* [Swish](neural_network/activation_functions/swish.py)
10551058
* [Back Propagation Neural Network](neural_network/back_propagation_neural_network.py)
10561059
* [Convolution Neural Network](neural_network/convolution_neural_network.py)
1060+
* [Lstm](neural_network/lstm.py)
10571061
* Optimizers
10581062
* [Adagrad](neural_network/optimizers/adagrad.py)
10591063
* [Adam Optimizer](neural_network/optimizers/adam_optimizer.py)

‎docs/hacktober_2026_prep.md‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Notation: `<index>. [ ] #<pr_id> <labels>`
8484
70. [x] #14939 merged
8585
71. [x] #14938 closed by close_pull_requests_with_failing_tests.sh
8686
72. [x] #14925 merged
87-
73. [ ] #14923 enhancement, awaiting reviews
87+
73. [x] #14923 closed
8888
74. [x] #14920 closed
8989
75. [x] #14914 closed
9090
76. [x] #14909 closed
@@ -363,7 +363,7 @@ Notation: `<index>. [ ] #<pr_id> <labels>`
363363
349. [x] #14021 closed
364364
350. [x] #14019 closed by close_pull_requests_with_require_tests.sh
365365
351. [x] #14016 closed
366-
352. [ ] #14015 awaiting reviews
366+
352. [x] #14015 closed
367367
353. [x] #14014 closed by close_pull_requests_with_require_type_hints.sh
368368
354. [ ] #14009 awaiting reviews
369369
355. [ ] #14008 awaiting reviews
@@ -681,7 +681,7 @@ Notation: `<index>. [ ] #<pr_id> <labels>`
681681
667. [x] #13009 closed by close_pull_requests_with_failing_tests.sh
682682
668. [x] #13007 closed
683683
669. [x] #13002 closed
684-
670. [ ] #13000 awaiting reviews
684+
670. [x] #13000 merged
685685
671. [x] #12999 merged
686686
672. [x] #12998 closed
687687
673. [x] #12989 closed
@@ -785,7 +785,7 @@ Notation: `<index>. [ ] #<pr_id> <labels>`
785785
771. [x] #12104 merged
786786
772. [x] #12100 closed
787787
773. [x] #12091 merged
788-
774. [ ] #12082 awaiting reviews
788+
774. [x] #12082 merged
789789
775. [x] #12066 merged
790790
776. [x] #12056 merged
791791
777. [x] #12042 closed
@@ -1129,7 +1129,7 @@ data_structures:
11291129
45. [x] #13802 closed
11301130
46. [ ] #14008
11311131
47. [ ] #14009
1132-
48. [ ] #14015
1132+
48. [x] #14015 closed
11331133
49. [ ] #14026
11341134
50. [x] #14048 closed
11351135
51. [x] #14186 merged
@@ -1361,7 +1361,7 @@ machine_learning:
13611361
55. [x] #14853 closed
13621362
56. [x] #14858 merged
13631363
57. [x] #14874 closed
1364-
58. [ ] #14923
1364+
58. [x] #14923 closed
13651365

13661366
maths:
13671367
1. [x] #9927 merged
@@ -1488,7 +1488,7 @@ networking_flow:
14881488
1. [x] #13128 merged
14891489

14901490
neural_network:
1491-
1. [ ] #12082
1491+
1. [x] #12082 merged
14921492
2. [x] #12214 merged
14931493
3. [x] #12421 closed
14941494
4. [x] #13133 merged
@@ -1730,7 +1730,7 @@ triage:
17301730
web_programming:
17311731
1. [x] #11193 closed
17321732
2. [x] #11655 merged
1733-
3. [ ] #13000
1733+
3. [x] #13000 merged
17341734
4. [x] #13016 merged
17351735
5. [x] #13797 merged
17361736

@@ -2063,16 +2063,16 @@ I can take a second pass through the remaining `awaiting triage` items (relabel
20632063

20642064
_Generated automatically by `scripts/hacktoberfest_prep_update.py` on 2026-09-24 (UTC)._
20652065

2066-
- **Open issues:** 10
2066+
- **Open issues:** 9
20672067
- **Open pull requests:** 120
2068-
- **Open PRs labelled `awaiting reviews`:** 87
2068+
- **Open PRs labelled `awaiting reviews`:** 88
20692069
- **Days until Hacktoberfest (2026-10-01):** 7
20702070
- **Issues to close per day to clear the backlog:** 2 per day (over 7 days)
20712071
- **Pull requests to merge or close per day to clear the backlog:** 18 per day (over 7 days)
20722072

20732073
**Top three directories to work on** (most open pull requests labelled `awaiting reviews`):
20742074

2075-
1. `data_structures/` — 14 awaiting-reviews PRs
2075+
1. `data_structures/` — 13 awaiting-reviews PRs
20762076
2. `maths/` — 12 awaiting-reviews PRs
2077-
3. `dynamic_programming/` — 9 awaiting-reviews PRs
2077+
3. `sorts/` — 10 awaiting-reviews PRs
20782078

‎hashes/jenkins_one_at_a_time.py‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
The Bob Jenkins hash is a fast, non-cryptographic hash function
3+
designed for general-purpose use, such as hash table lookups.
4+
5+
https://en.wikipedia.org/wiki/Jenkins_hash_function
6+
"""
7+
8+
9+
def jenkins_one_at_a_time(key: str) -> int:
10+
"""
11+
Calculate Jenkins One-at-a-Time hash for a key.
12+
13+
>>> jenkins_one_at_a_time("apple")
14+
2297466611
15+
>>> jenkins_one_at_a_time("test")
16+
1064684737
17+
>>> jenkins_one_at_a_time("")
18+
0
19+
"""
20+
hash_value = 0
21+
mask = 0xFFFFFFFF
22+
key_bytes = key.encode("utf-8")
23+
24+
for byte in key_bytes:
25+
hash_value = (hash_value + byte) & mask
26+
hash_value = (hash_value + (hash_value << 10)) & mask
27+
hash_value = (hash_value ^ (hash_value >> 6)) & mask
28+
29+
hash_value = (hash_value + (hash_value << 3)) & mask
30+
hash_value = (hash_value ^ (hash_value >> 11)) & mask
31+
hash_value = (hash_value + (hash_value << 15)) & mask
32+
33+
return hash_value
34+
35+
36+
if __name__ == "__main__":
37+
import doctest
38+
39+
doctest.testmod()

‎maths/collatz_sequence.py‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ def collatz_sequence(n: int) -> Generator[int]:
2323
>>> tuple(collatz_sequence(2.1))
2424
Traceback (most recent call last):
2525
...
26-
Exception: Sequence only defined for positive integers
26+
TypeError: Sequence only defined for integers
2727
>>> tuple(collatz_sequence(0))
2828
Traceback (most recent call last):
2929
...
30-
Exception: Sequence only defined for positive integers
30+
ValueError: Sequence only defined for positive integers
3131
>>> tuple(collatz_sequence(4))
3232
(4, 2, 1)
3333
>>> tuple(collatz_sequence(11))
@@ -44,8 +44,10 @@ def collatz_sequence(n: int) -> Generator[int]:
4444
(43, 130, 65, 196, 98, 49, 148, 74, 37, 112, 56, 28, 14, 7, 22, 11, 34, 17, 52, 26,
4545
13, 40, 20, 10, 5, 16, 8, 4, 2, 1)
4646
"""
47-
if not isinstance(n, int) or n < 1:
48-
raise Exception("Sequence only defined for positive integers")
47+
if not isinstance(n, int):
48+
raise TypeError("Sequence only defined for integers")
49+
if n < 1:
50+
raise ValueError("Sequence only defined for positive integers")
4951

5052
yield n
5153
while n != 1:

‎maths/sum_of_digits.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def sum_of_digits_recursion(n: int) -> int:
3131
0
3232
"""
3333
n = abs(n)
34-
return n if n < 10 else n % 10 + sum_of_digits(n // 10)
34+
return n if n < 10 else n % 10 + sum_of_digits_recursion(n // 10)
3535

3636

3737
def sum_of_digits_compact(n: int) -> int:

‎matrix/similar_matrices.py‎

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
"""Determine whether two square matrices are similar.
2+
3+
Two square matrices :math:`A` and :math:`B` of the same size are similar if
4+
there exists an invertible matrix :math:`P` such that :math:`P^{-1} A P = B`.
5+
This implementation relies on SymPy to compute the Jordan canonical form of
6+
both matrices. Two matrices are similar precisely when their Jordan forms are
7+
equal up to permutation of the Jordan blocks.
8+
* https://en.wikipedia.org/wiki/Jordan_matrix
9+
* https://en.wikipedia.org/wiki/Jordan_normal_form
10+
11+
Examples
12+
--------
13+
>>> are_similar_matrices([[3, 1], [0, 3]], [[3, 0], [0, 3]])
14+
False
15+
>>> from sympy import Matrix
16+
>>> matrix_a = [[3, 1], [0, 3]]
17+
>>> transform = Matrix([[1, 1], [0, 1]])
18+
>>> matrix_b = (transform.inv() * Matrix(matrix_a) * transform).tolist()
19+
>>> are_similar_matrices(matrix_a, matrix_b)
20+
True
21+
>>> are_similar_matrices(
22+
... [[1, 2, 0], [0, 1, 0], [0, 0, 3]],
23+
... [[1, 0, 0], [0, 1, 0], [0, 0, 3]],
24+
... )
25+
False
26+
>>> are_similar_matrices([[1, 2], [0, 1]], [[1, 2, 0], [0, 1, 0]])
27+
Traceback (most recent call last):
28+
...
29+
ValueError: both matrices must be square with the same dimensions
30+
"""
31+
32+
from __future__ import annotations
33+
34+
from collections.abc import Sequence
35+
from typing import Any
36+
37+
from sympy import Matrix, nsimplify
38+
from sympy.matrices.common import MatrixError
39+
40+
__all__ = ["are_similar_matrices"]
41+
42+
43+
type MatrixLike = Sequence[Sequence[Any]] | Matrix
44+
45+
46+
def _as_square_matrix(matrix: MatrixLike, *, simplify_entries: bool) -> Matrix:
47+
"""Return a SymPy matrix after validating that ``matrix`` is square.
48+
49+
Parameters
50+
----------
51+
matrix:
52+
Nested sequences (or a SymPy matrix) describing the matrix entries.
53+
simplify_entries:
54+
When ``True`` each entry is passed through :func:`sympy.nsimplify`
55+
which helps treat values such as ``0.5`` and ``1/2`` as the same.
56+
57+
Raises
58+
------
59+
TypeError
60+
If ``matrix`` cannot be converted into a SymPy matrix.
61+
ValueError
62+
If ``matrix`` is not square.
63+
"""
64+
65+
try:
66+
sympy_matrix = Matrix(matrix)
67+
except (TypeError, ValueError) as exc: # pragma: no cover - defensive
68+
msg = "matrix input must be a rectangular sequence of numbers"
69+
raise TypeError(msg) from exc
70+
71+
if sympy_matrix.rows != sympy_matrix.cols:
72+
raise ValueError("both matrices must be square with the same dimensions")
73+
74+
if simplify_entries:
75+
sympy_matrix = sympy_matrix.applyfunc(nsimplify)
76+
77+
return sympy_matrix
78+
79+
80+
def _jordan_signature(matrix: Matrix) -> tuple[tuple[Any, tuple[int, ...]], ...]:
81+
"""Return a hashable representation of the Jordan form of ``matrix``."""
82+
83+
_, blocks = matrix.jordan_cells()
84+
summary: dict[Any, list[int]] = {}
85+
for block in blocks:
86+
block_matrix = Matrix(block)
87+
eigenvalue = block_matrix[0, 0]
88+
summary.setdefault(eigenvalue, []).append(block_matrix.rows)
89+
90+
return tuple(
91+
(
92+
eigenvalue,
93+
tuple(sorted(block_sizes, reverse=True)),
94+
)
95+
for eigenvalue, block_sizes in sorted(
96+
summary.items(), key=lambda item: repr(item[0])
97+
)
98+
)
99+
100+
101+
def are_similar_matrices(
102+
matrix_a: MatrixLike,
103+
matrix_b: MatrixLike,
104+
*,
105+
simplify_entries: bool = True,
106+
) -> bool:
107+
"""Return ``True`` if ``matrix_a`` and ``matrix_b`` are similar matrices.
108+
109+
Parameters
110+
----------
111+
matrix_a, matrix_b:
112+
Square matrices represented as nested sequences (or SymPy matrices).
113+
simplify_entries:
114+
If ``True`` (default), the function attempts to simplify each entry so
115+
that values that are algebraically equal are treated as such. Set this
116+
to ``False`` to skip simplification when working with symbolic inputs
117+
that should remain untouched.
118+
119+
Raises
120+
------
121+
ValueError
122+
If the matrices are not square or their dimensions do not match.
123+
TypeError
124+
If either matrix cannot be interpreted as a numeric matrix.
125+
"""
126+
127+
sympy_a = _as_square_matrix(matrix_a, simplify_entries=simplify_entries)
128+
sympy_b = _as_square_matrix(matrix_b, simplify_entries=simplify_entries)
129+
130+
if sympy_a.shape != sympy_b.shape:
131+
raise ValueError("both matrices must be square with the same dimensions")
132+
133+
try:
134+
signature_a = _jordan_signature(sympy_a)
135+
signature_b = _jordan_signature(sympy_b)
136+
except MatrixError as exc: # pragma: no cover - rare SymPy failure
137+
msg = "unable to determine the Jordan canonical form"
138+
raise ValueError(msg) from exc
139+
140+
return signature_a == signature_b
141+
142+
143+
if __name__ == "__main__": # pragma: no cover - convenience execution
144+
from doctest import testmod
145+
146+
testmod()

0 commit comments

Comments
 (0)