Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add chinese remainder theorem #759

Merged
merged 13 commits into from
Mar 11, 2021
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ If you want to uninstall algorithms, it is as simple as:
- [is_anagram](algorithms/map/is_anagram.py)
- [maths](algorithms/maths)
- [base_conversion](algorithms/maths/base_conversion.py)
- [chinese_remainder_theorem](algorithms/maths/chinese_remainder_theorem.py)
- [combination](algorithms/maths/combination.py)
- [cosine_similarity](algorithms/maths/cosine_similarity.py)
- [decimal_to_binary_ip](algorithms/maths/decimal_to_binary_ip.py)
Expand Down
46 changes: 46 additions & 0 deletions algorithms/maths/chinese_remainder_theorem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from algorithms.maths.gcd import gcd
from typing import List

def solve_chinese_remainder(num : List[int], rem : List[int]):
"""
Computes the smallest x that satisfies the chinese remainder theorem
for a system of equations.
The system of equations has the form:
x % num[0] = rem[0]
x % num[1] = rem[1]
...
x % num[k - 1] = rem[k - 1]
Where k is the number of elements in num and rem, k > 0.
All numbers in num needs to be pariwise coprime otherwise an exception is raised
returns x: the smallest value for x that satisfies the system of equations
"""
if not len(num) == len(rem):
raise Exception("num and rem should have equal length")
if not len(num) > 0:
raise Exception("Lists num and rem need to contain at least one element")
for n in num:
if not n > 1:
raise Exception("All numbers in num needs to be > 1")
if not _check_coprime(num):
raise Exception("All pairs of numbers in num are not coprime")
k = len(num)
x = 1
while True:
i = 0
while i < k:
if x % num[i] != rem[i]:
break
i += 1
if i == k:
return x
else:
x += 1

def _check_coprime(l : List[int]):
for i in range(len(l)):
for j in range(len(l)):
if i == j:
continue
if gcd(l[i], l[j]) != 1:
return False
return True
33 changes: 32 additions & 1 deletion tests/test_maths.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
alice_private_key, alice_public_key, bob_private_key, bob_public_key, alice_shared_key, bob_shared_key, diffie_hellman_key_exchange,
num_digits,
alice_private_key, alice_public_key, bob_private_key, bob_public_key, alice_shared_key, bob_shared_key,
diffie_hellman_key_exchange, krishnamurthy_number
diffie_hellman_key_exchange, krishnamurthy_number,
chinese_remainder_theorem,
)

import unittest
Expand Down Expand Up @@ -465,6 +466,36 @@ def test_num_digits(self):
self.assertEqual(1,num_digits(-5))
self.assertEqual(3,num_digits(-254))

class TestChineseRemainderSolver(unittest.TestCase):
def test_k_three(self):
# Example which should give the answer 143
# which is the smallest possible x that
# solves the system of equations
num = [3, 7, 10]
rem = [2, 3, 3]
self.assertEqual(chinese_remainder_theorem.solve_chinese_remainder(num, rem), 143)

def test_k_five(self):
# Example which should give the answer 3383
# which is the smallest possible x that
# solves the system of equations
num = [3, 5, 7, 11, 26]
rem = [2, 3, 2, 6, 3]
self.assertEqual(chinese_remainder_theorem.solve_chinese_remainder(num, rem), 3383)

def test_exception_non_coprime(self):
# There should be an exception when all
# numbers in num are not pairwise coprime
num = [3, 7, 10, 14]
rem = [2, 3, 3, 1]
with self.assertRaises(Exception):
chinese_remainder_theorem.solve_chinese_remainder(num, rem)

def test_empty_lists(self):
num = []
rem = []
with self.assertRaises(Exception):
chinese_remainder_theorem.solve_chinese_remainder(num, rem)

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