Reputation: 77
I have equation of form a+b*n1=c+d*n2
, where a,b,c,d are known numbers with around 1000 digits and I need to solve n1.
I tried:
i=1
while True:
a=(a+b)%d
if(a==c):
break
i+=1
print(i)
, but this method is too slow for numbers this big. Is there some better method to use in this kind of situations?
Upvotes: 1
Views: 405
Reputation: 58320
You want to find x
such that x = a (mod b)
and x = c (mod d)
. For then, n1 = (x - a) / b
and n2 = (x - c) / d
.
If b
and d
are coprime, then the existence of x
is guaranteed by the Chinese Remainder Theorem -- and a solution can be found using the Extended Euclidean Algorithm.
If b
and d
aren't coprime (that is, if gcd(b, d) != 1
), then (noting that a = c (mod gcd(b, d))
), we can subtract a % gcd(b, d)
from both sides, and divide through by gcd(b, d)
to reduce to a problem as above.
Here's code that finds n1
and n2
using this method:
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def modinv(a, m):
return egcd(a, m)[1] % m
def solve(a, b, c, d):
gcd = egcd(b, d)[0]
if gcd != 1:
if a % gcd != c % gcd:
raise ValueError('no solution')
a, c = a - a % gcd, c - c % gcd
a //= gcd
b //= gcd
c //= gcd
d //= gcd
x = a * d * modinv(d, b) + c * b * modinv(b, d)
return (x - a) // b, (x - c) // d
And here's some test code that runs 1000 random trials of 1000-digit inputs:
import sys
sys.setrecursionlimit(10000)
import random
digit = '0123456789'
def rn(k):
return int(''.join(random.choice(digit) for _ in xrange(k)), 10)
k = 1000
for _ in xrange(1000):
a, b, c, d, = rn(k), rn(k), rn(k), rn(k)
print a, b, c, d
try:
n1, n2 = solve(a, b, c, d)
except ValueError, exn:
print 'no solution'
print
continue
if a + b * n1 != c + d * n2:
raise AssertionError('failed!')
print 'found solution:', n1, n2
print
(Note, the recursion limit has to be increased because the egcd
function which implements the Extended Euclidean algorithm is recursive, and running it on 1000 digit numbers can require a quite deep stack).
Also note, that this checks the result when a solution is returned. But when a != c (mod gcd(b, d)) and the exception is raised signalling no result, no check is done. So you need to think through if this can fail to find results when solutions do exist.
This runs (1000 trials) in around 7-8 seconds on my machine, so it performs reasonably well.
Upvotes: 1