solveLinearCongruences.py 1.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. def extended_euclidean_algorithm(a, b):
  4. """
  5. Calculates gcd(a,b) and a linear combination such that
  6. gcd(a,b) = a*x + b*y
  7. As a side effect:
  8. If gcd(a,b) = 1 = a*x + b*y
  9. Then x is multiplicative inverse of a modulo b.
  10. """
  11. aO, bO = a, b
  12. x = lasty = 0
  13. y = lastx = 1
  14. while (b != 0):
  15. q = a/b
  16. a, b = b, a % b
  17. x, lastx = lastx-q*x, x
  18. y, lasty = lasty-q*y, y
  19. return {
  20. "x": lastx,
  21. "y": lasty,
  22. "gcd": aO * lastx + bO * lasty
  23. }
  24. def solve_linear_congruence_equations(rests, modulos):
  25. """
  26. Solve a system of linear congruences.
  27. Examples
  28. --------
  29. >>> solve_linear_congruence_equations([4, 12, 14], [19, 37, 43])
  30. {'congruence class': 22804, 'modulo': 30229}
  31. """
  32. assert len(rests) == len(modulos)
  33. x = 0
  34. M = reduce(lambda x, y: x*y, modulos)
  35. for mi, resti in zip(modulos, rests):
  36. Mi = M / mi
  37. s = extended_euclidean_algorithm(Mi, mi)["x"]
  38. e = s * Mi
  39. x += resti * e
  40. return {"congruence class": ((x % M) + M) % M, "modulo": M}
  41. if __name__ == "__main__":
  42. import doctest
  43. doctest.testmod()