Solving modular linear congruences for large numbers

后端 未结 2 1543
后悔当初
后悔当初 2021-01-24 23:05

I\'m looking for a better algorithm than one I found on stackoverflow to handle 4096 byte numbers, i\'m hitting a maximum recursion depth.

Code from stackoverlow post, i

2条回答
  •  花落未央
    2021-01-25 00:01

    If you have Python 3.8 or later, you can do everything you need to with a very small number of lines of code.

    First some mathematics: I'm assuming that you want to solve ax = b (mod m) for an integer x, given integers a, b and m. I'm also assuming that m is positive.

    The first thing you need to compute is the greatest common divisor g of a and m. There are two cases:

    • if b is not a multiple of g, then the congruence has no solutions (if ax + my = b for some integers x and y, then any common divisor of a and m must also be a divisor of b)

    • if b is a multiple of g, then the congruence is exactly equivalent to (a/g)x = (b/g) (mod (m/g)). Now a/g and m/g are relatively prime, so we can compute an inverse to a/g modulo m/g. Multiplying that inverse by b/g gives a solution, and the general solution can be obtained by adding an arbitrary multiple of m/g to that solution.

    Python's math module has had a gcd function since Python 3.5, and the built-in pow function can be used to compute modular inverses since Python 3.8.

    Putting it all together, here's some code. First a function that finds the general solution, or raises an exception if no solution exists. If it succeeds, it returns two integers. The first gives a particular solution; the second gives the modulus that provides the general solution.

    def solve_linear_congruence(a, b, m):
        """ Describe all solutions to ax = b  (mod m), or raise ValueError. """
        g = math.gcd(a, m)
        if b % g:
            raise ValueError("No solutions")
        a, b, m = a//g, b//g, m//g
        return pow(a, -1, m) * b % m, m
    

    And then some driver code, to demonstrate how to use the above.

    def print_solutions(a, b, m):
        print(f"Solving the congruence: {a}x = {b}  (mod {m})")
        try:
            x, mx = solve_linear_congruence(a, b, m)
        except ValueError:
            print("No solutions")
        else:
            print(f"Particular solution: x = {x}")
            print(f"General solution: x = {x}  (mod {mx})")
    

    Example use:

    >>> print_solutions(272, 256, 1009)
    Solving the congruence: 272x = 256  (mod 1009)
    Particular solution: x = 179
    General solution: x = 179  (mod 1009)
    >>> print_solutions(98, 105, 1001)
    Solving the congruence: 98x = 105  (mod 1001)
    Particular solution: x = 93
    General solution: x = 93  (mod 143)
    >>> print_solutions(98, 107, 1001)
    Solving the congruence: 98x = 107  (mod 1001)
    No solutions
    

提交回复
热议问题