Skip to content

Commit 8f6e5f0

Browse files
Add chinese remainder theorem (keon#759)
* feat: Add basic ch. remainder theorem algorithm * feat: Add all n coprime check Co-authored-by: Lazar Cerovic<lazarc@kth.se> * Add gcd function * Add list length > 0 check * doc: Improve function documentation * feat: add all divisors need to be > 1 * test: Add test cases for crt solver * fix: make check_coprime private * fix: Change to python3.7 type hints * refactor: Move ch. remainder theorem tests to test_maths * Add link in README * Remove unnecessary whitespace and add newline at end of file * docs: Fix README alphabetic order
1 parent 6adcb36 commit 8f6e5f0

File tree

3 files changed

+79
-1
lines changed

3 files changed

+79
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ If you want to uninstall algorithms, it is as simple as:
200200
- [is_anagram](algorithms/map/is_anagram.py)
201201
- [maths](algorithms/maths)
202202
- [base_conversion](algorithms/maths/base_conversion.py)
203+
- [chinese_remainder_theorem](algorithms/maths/chinese_remainder_theorem.py)
203204
- [combination](algorithms/maths/combination.py)
204205
- [cosine_similarity](algorithms/maths/cosine_similarity.py)
205206
- [decimal_to_binary_ip](algorithms/maths/decimal_to_binary_ip.py)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from algorithms.maths.gcd import gcd
2+
from typing import List
3+
4+
def solve_chinese_remainder(num : List[int], rem : List[int]):
5+
"""
6+
Computes the smallest x that satisfies the chinese remainder theorem
7+
for a system of equations.
8+
The system of equations has the form:
9+
x % num[0] = rem[0]
10+
x % num[1] = rem[1]
11+
...
12+
x % num[k - 1] = rem[k - 1]
13+
Where k is the number of elements in num and rem, k > 0.
14+
All numbers in num needs to be pariwise coprime otherwise an exception is raised
15+
returns x: the smallest value for x that satisfies the system of equations
16+
"""
17+
if not len(num) == len(rem):
18+
raise Exception("num and rem should have equal length")
19+
if not len(num) > 0:
20+
raise Exception("Lists num and rem need to contain at least one element")
21+
for n in num:
22+
if not n > 1:
23+
raise Exception("All numbers in num needs to be > 1")
24+
if not _check_coprime(num):
25+
raise Exception("All pairs of numbers in num are not coprime")
26+
k = len(num)
27+
x = 1
28+
while True:
29+
i = 0
30+
while i < k:
31+
if x % num[i] != rem[i]:
32+
break
33+
i += 1
34+
if i == k:
35+
return x
36+
else:
37+
x += 1
38+
39+
def _check_coprime(l : List[int]):
40+
for i in range(len(l)):
41+
for j in range(len(l)):
42+
if i == j:
43+
continue
44+
if gcd(l[i], l[j]) != 1:
45+
return False
46+
return True

tests/test_maths.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
alice_private_key, alice_public_key, bob_private_key, bob_public_key, alice_shared_key, bob_shared_key, diffie_hellman_key_exchange,
2626
num_digits,
2727
alice_private_key, alice_public_key, bob_private_key, bob_public_key, alice_shared_key, bob_shared_key,
28-
diffie_hellman_key_exchange, krishnamurthy_number
28+
diffie_hellman_key_exchange, krishnamurthy_number,
29+
chinese_remainder_theorem,
2930
)
3031

3132
import unittest
@@ -496,6 +497,36 @@ def test_num_digits(self):
496497
self.assertEqual(1,num_digits(-5))
497498
self.assertEqual(3,num_digits(-254))
498499

500+
class TestChineseRemainderSolver(unittest.TestCase):
501+
def test_k_three(self):
502+
# Example which should give the answer 143
503+
# which is the smallest possible x that
504+
# solves the system of equations
505+
num = [3, 7, 10]
506+
rem = [2, 3, 3]
507+
self.assertEqual(chinese_remainder_theorem.solve_chinese_remainder(num, rem), 143)
508+
509+
def test_k_five(self):
510+
# Example which should give the answer 3383
511+
# which is the smallest possible x that
512+
# solves the system of equations
513+
num = [3, 5, 7, 11, 26]
514+
rem = [2, 3, 2, 6, 3]
515+
self.assertEqual(chinese_remainder_theorem.solve_chinese_remainder(num, rem), 3383)
516+
517+
def test_exception_non_coprime(self):
518+
# There should be an exception when all
519+
# numbers in num are not pairwise coprime
520+
num = [3, 7, 10, 14]
521+
rem = [2, 3, 3, 1]
522+
with self.assertRaises(Exception):
523+
chinese_remainder_theorem.solve_chinese_remainder(num, rem)
524+
525+
def test_empty_lists(self):
526+
num = []
527+
rem = []
528+
with self.assertRaises(Exception):
529+
chinese_remainder_theorem.solve_chinese_remainder(num, rem)
499530

500531
if __name__ == "__main__":
501532
unittest.main()

0 commit comments

Comments
 (0)