solveLinearCongruences.py 1.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. def ExtendedEuclideanAlgorithm(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 solveLinearCongruenceEquations(rests, modulos):
  25. """
  26. Solve a system of linear congruences.
  27. >>> solveLinearCongruenceEquations([4, 12, 14], [19, 37, 43])
  28. {'congruence class': 22804, 'modulo': 30229}
  29. """
  30. assert len(rests) == len(modulos)
  31. x = 0
  32. M = reduce(lambda x, y: x*y, modulos)
  33. for mi, resti in zip(modulos, rests):
  34. Mi = M / mi
  35. s = ExtendedEuclideanAlgorithm(Mi, mi)["x"]
  36. e = s * Mi
  37. x += resti * e
  38. return {"congruence class": ((x % M) + M) % M, "modulo": M}
  39. if __name__ == "__main__":
  40. import doctest
  41. doctest.testmod()