Skip to content

Commit 20d200b

Browse files
authored
Merge pull request #12 from rlishtaba/feature/new_algorithms
Adding primality tests & string search algorithms
2 parents 5c1bee2 + d67a04b commit 20d200b

File tree

14 files changed

+354
-21
lines changed

14 files changed

+354
-21
lines changed

README.md

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,17 @@ $ pip install py-algorithms
4949
- Selection Sort
5050
---
5151

52+
^ String algorithms
53+
- Patttern Matching
54+
- Boye-Moor Find
55+
- Knut-Morris-Prat Find
56+
---
57+
58+
^ Primality Tests
59+
- Miller-Rabin primality test
60+
- Simple primality test
61+
---
62+
5263
^ Algorithms
5364
- Quick Union
5465
- Union Find
@@ -370,19 +381,15 @@ Make a new heap with `Max` property by supplying functor `(a: any, b: any) => c:
370381
from py_algorithms.data_structures import new_heap, Heap
371382

372383
heap = new_heap(lambda x, y: (x > y) - (x < y) == 1) # type: Heap
373-
```
374384

375-
Push distinct key value pairs to the heap
385+
# Push distinct key value pairs to the heap
376386

377-
```python
378387
heap.push('Kelly', 1)
379388
heap.push('Susan', 8)
380389
heap.push('Ryan', 7)
381-
```
382390

383-
Heap should manage to keep highest key & value on the top
391+
# Heap should manage to keep highest key & value on the top
384392

385-
```python
386393
heap.next_key #=> 'Susan'
387394
heap.pop() #=> 8
388395
```
@@ -397,11 +404,9 @@ heap = new_max_heap() # type: Heap
397404
heap.push('Kelly', 1)
398405
heap.push('Susan', 8)
399406
heap.push('Ryan', 7)
400-
```
401407

402-
Heap should manage to keep highest key & value on the top
408+
# Heap should manage to keep highest key & value on the top
403409

404-
```python
405410
heap.next_key #=> 'Susan'
406411
heap.max #=> 8
407412
heap.pop() #=> 8
@@ -417,11 +422,9 @@ heap = new_min_heap() # type: Heap
417422
heap.push('One', 1)
418423
heap.push('Eight', -8)
419424
heap.push('Seven', 7)
420-
```
421425

422-
Heap should manage to keep lowest key & value on the top
426+
# Heap should manage to keep lowest key & value on the top
423427

424-
```python
425428
heap.next_key #=> 'Eight'
426429
heap.min #=> -8
427430
heap.pop() #=> -8
@@ -469,6 +472,83 @@ ds.is_sub_str('blah') #=> False
469472

470473
---
471474

475+
---
476+
477+
### String Algorithms
478+
479+
480+
481+
![boyer-moore-media](https://www.researchgate.net/profile/Monther_Aldwairi/publication/237067573/figure/fig1/AS:299315785420823@1448373852591/Figure-1-The-Boyer-Moore-algorithm-illustrated-while-checking-a-signature-in-Snort.png)
482+
483+
#### Boyer–Moore string search algorithm
484+
485+
In computer science, the Boyer–Moore string search algorithm is an efficient string searching algorithm that
486+
is the standard benchmark for practical string search literature. It was developed by Robert S. Boyer and J Strother Moore in 1977.
487+
The algorithm preprocesses the string being searched for (the pattern), but not the string being searched in (the text).
488+
It is thus well-suited for applications in which the pattern is much shorter than the text or where it persists across multiple searches.
489+
The Boyer-Moore algorithm uses information gathered during the preprocess step to skip sections of the text,
490+
resulting in a lower constant factor than many other string search algorithms.
491+
492+
```python
493+
494+
from py_algorithms.strings import new_boyer_moore_find
495+
496+
algorithm = new_boyer_moore_find()
497+
498+
substr = 'abcd'
499+
algorithm('foo' + substr + 'bar', substr) # => 3
500+
501+
```
502+
503+
---
504+
505+
### Primality Test Algorithms
506+
507+
[primes](https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Gaussian_integer_lattice.svg/389px-Gaussian_integer_lattice.svg.png)
508+
509+
A primality test is an algorithm for determining whether an input number is prime.
510+
Among other fields of mathematics, it is used for cryptography.
511+
Unlike integer factorization, primality tests do not generally give prime factors, only stating whether the input number
512+
is prime or not. Factorization is thought to be a computationally difficult problem, whereas primality
513+
testing is comparatively easy (its running time is polynomial in the size of the input).
514+
Some primality tests prove that a number is prime, while others like Miller–Rabin prove that a number is composite.
515+
Therefore, the latter might be called compositeness tests instead of primality tests.
516+
517+
518+
#### Miller-Rabin Primality Test
519+
520+
The Miller–Rabin primality test or Rabin–Miller primality test is a primality test: an algorithm which determines whether a given number is prime,
521+
similar to the Fermat primality test and the Solovay–Strassen primality test. Its original version, due to Gary L. Miller, is deterministic,
522+
but the correctness relies on the unproven Extended Riemann hypothesis; Michael O. Rabin modified it to obtain an unconditional probabilistic algorithm.
523+
524+
```python
525+
from py_algorithms.primality_tests import new_miller_rabin_primality_test
526+
527+
algorithm = new_miller_rabin_primality_test()
528+
529+
algorithm(199) #=> True, 199 is a prime number
530+
algorithm(4) #=> False, 4 % 2 == 0, therefore, 4 is not a prime number.
531+
532+
```
533+
534+
#### Simple Primality Test
535+
536+
The test itself is simple and straightforward, but would work very slow on quite big primes.
537+
538+
539+
```python
540+
from py_algorithms.primality_tests import new_simple_primality_test
541+
542+
algorithm = new_simple_primality_test()
543+
544+
algorithm(32416190071) #=> True, 32416190071 is a prime number
545+
algorithm(4) #=> False, 4 % 2 == 0, therefore, 4 is not a prime number.
546+
547+
```
548+
549+
---
550+
551+
472552
## Contributing
473553

474554
Bug reports and pull requests are welcome on GitHub at https://github.com/rlishtaba/py-algorithms. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.

py_algorithms/challenges/various/__init__.py

Whitespace-only changes.

py_algorithms/graph/__init__.py

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
__all__ = [
2+
'new_miller_rabin_primality_test',
3+
'new_simple_primality_test'
4+
]
5+
6+
from typing import Callable
7+
from .miller_rabin_primality_test import MillerRabinPrimalityTest
8+
from .simple_primality_test import SimplePrimalityTest
9+
10+
11+
def new_miller_rabin_primality_test() -> Callable[[int, int], bool]:
12+
return MillerRabinPrimalityTest()
13+
14+
15+
def new_simple_primality_test() -> Callable[[int], bool]:
16+
return SimplePrimalityTest()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from random import randrange
2+
3+
4+
class MillerRabinPrimalityTest:
5+
def __call__(self, n: int, k: int = 10) -> bool:
6+
if n == 2:
7+
return True
8+
if not n & 1:
9+
return False
10+
11+
def check(a, s, d, n):
12+
x = pow(a, d, n)
13+
if x == 1:
14+
return True
15+
for i in range(0, s - 1):
16+
if x == n - 1:
17+
return True
18+
x = pow(x, 2, n)
19+
return x == n - 1
20+
21+
s = 0
22+
d = n - 1
23+
24+
while d % 2 == 0:
25+
d >>= 1
26+
s += 1
27+
28+
for _ in range(0, k):
29+
a = randrange(2, n)
30+
if not check(a, s, d, n):
31+
return False
32+
return True
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class SimplePrimalityTest:
2+
def __call__(self, num: int) -> bool:
3+
if num <= 1:
4+
return False
5+
if num <= 3:
6+
return True
7+
8+
if num % 2 == 0 or num % 3 == 0:
9+
return False
10+
11+
i = 5
12+
while i * i < num:
13+
if num % i == 0 or num % (i + 2) == 0:
14+
return True
15+
i += 6
16+
17+
return True

py_algorithms/strings/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
__all__ = [
2+
'new_boyer_moore_find'
3+
]
4+
5+
from typing import Callable
6+
from .boyer_moore_find import BoyerMooreFind
7+
8+
9+
def new_boyer_moore_find() -> Callable[[str, str], int]:
10+
return BoyerMooreFind()
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
class BoyerMooreFind:
2+
def __call__(self, string, substring) -> int:
3+
"""
4+
5+
:param string: a string to perform search in.
6+
:param substring: a string to find in a target.
7+
:return: index of start position if match found, otherwise -1.
8+
"""
9+
10+
n = len(string)
11+
m = len(substring)
12+
13+
if m == 0:
14+
return 0
15+
16+
last = {}
17+
for k in range(0, m):
18+
last[substring[k]] = k
19+
20+
i = m - 1
21+
k = m - 1
22+
23+
while i < n:
24+
if string[i] == substring[k]:
25+
if k == 0:
26+
return i
27+
else:
28+
i -= 1
29+
k -= 1
30+
else:
31+
j = last.get(string[i], -1)
32+
i += m - min(k, j + 1)
33+
k = m - 1
34+
35+
return -1

setup.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,25 @@
1818
keywords=['algorithm', 'algorithms', 'data structure', 'data structures',
1919
'data structures python', 'containers', 'heaps', 'heap sort',
2020
'sorting algorithms', 'graphs', 'graph algorithms', 'hashing',
21-
'python', 'selection sort', 'selectionsort', 'merge sort', 'mergesort',
22-
'bubble sort', 'bubblesort', 'quick sort', 'quick-sort', 'quicksort', 'qsort',
23-
'binary search', 'heap', 'fibonacci heap', 'max heap', 'min heap', 'priority queue',
24-
'fibonacci priority queue', 'max priority queue', 'min priority queue',
25-
'dynamic connectivity', 'union find', 'quick union', 'weighted quick union',
26-
'weighted quick union with path compression', 'fibonacci heap sort', 'heapsort',
27-
'heap sort', 'shell-sort', 'shell sort', 'shellsort', 'comb sort', 'comb-sort',
21+
'python', 'selection sort', 'selectionsort', 'merge sort',
22+
'mergesort',
23+
'bubble sort', 'bubblesort', 'quick sort', 'quick-sort',
24+
'quicksort', 'qsort',
25+
'binary search', 'heap', 'fibonacci heap', 'max heap', 'min heap',
26+
'priority queue',
27+
'fibonacci priority queue', 'max priority queue',
28+
'min priority queue',
29+
'dynamic connectivity', 'union find', 'quick union',
30+
'weighted quick union',
31+
'weighted quick union with path compression',
32+
'fibonacci heap sort', 'heapsort',
33+
'heap sort', 'shell-sort', 'shell sort', 'shellsort', 'comb sort',
34+
'comb-sort',
2835
'combsort', 'coderbyte', 'hackerrank', 'coderbyte challenges',
29-
'hackerrank challenges'],
36+
'hackerrank challenges', 'boyer-moore',
37+
'boyer-moore-string-search', 'primality-tes',
38+
'miller-rabin', 'miller-rabin-primality-test',
39+
'simple-primality-test'],
3040
description="Library of Algorithms, Data Structures, variety of solutions to common "
3141
"CS problems.",
3242
long_description="Library of Algorithms, Data Structures, variety of solutions to common "

tests/conftest.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,71 @@
11
import random
2-
from typing import List
2+
from typing import List, Tuple
33

44
import pytest
55

6+
BIG_PRIMES = (
7+
32416188037, 32416188691, 32416189321, 32416189919,
8+
32416188113, 32416188697, 32416189349, 32416189987,
9+
32416188127, 32416188767, 32416189361, 32416190039,
10+
32416188191, 32416188793, 32416189381, 32416190071)
11+
12+
PRIMES_0_3000 = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53,
13+
59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
14+
127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
15+
191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251,
16+
257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
17+
331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397,
18+
401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
19+
467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557,
20+
563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619,
21+
631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
22+
709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787,
23+
797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
24+
877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953,
25+
967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031,
26+
1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093,
27+
1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171,
28+
1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237,
29+
1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303,
30+
1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409,
31+
1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471,
32+
1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543,
33+
1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607,
34+
1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669,
35+
1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753,
36+
1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847,
37+
1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913,
38+
1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999,
39+
2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081,
40+
2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141,
41+
2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239,
42+
2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309,
43+
2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381,
44+
2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447,
45+
2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549,
46+
2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647,
47+
2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699,
48+
2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767,
49+
2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843,
50+
2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927,
51+
2939, 2953, 2957, 2963, 2969, 2971, 2999)
52+
653

754
@pytest.fixture
855
def xs() -> List[int]:
956
return [0, 6, 7, 8, 9, 4, 5, 12, -1]
1057

1158

59+
@pytest.fixture
60+
def primes_0_3000() -> Tuple[int, ...]:
61+
return PRIMES_0_3000
62+
63+
64+
@pytest.fixture
65+
def big_primes() -> Tuple[int, ...]:
66+
return BIG_PRIMES
67+
68+
1269
@pytest.fixture
1370
def large_xs() -> List[int]:
1471
raw = [0, 6, 7, 8, 9, 4, 5, 12, -112] * 500

0 commit comments

Comments
 (0)