Solving "Modular Search"
In Russ Cox' "Floating-Point Printing and Parsing Can Be Simple And Fast" (which is both a tough and an enjoyable read, the guy's a genius!), the following problem is introduced under the name "modular search":
For given positive integers , , find the value of such that is minimal and in the range .
I didn't quite like the solution that was presented there. It works, but I think there is a more elegant solution that is is the obvious one for people with some knowledge of modular arithmetic. I'll present this solution here.
It is well-known that the expression can only assume values that are multiples of . So, it is easy to check if the condition can be satisfied: We round up to a multiple of to obtain a "candidate" value. If this candidate value is more than , we know the problem has no solution for the given and .
If the candidate is less than or equal to , we know the problem has a solution and we want to find the such that equals our candidate value modulo .
It is also well-known that by using the extended Euclidean algorithm we can efficiently find a such that modulo . So we have
So we find that .
All in all, we get the following:
def minmax_euclid(c, m, min, max):
gcd, _, d = extended_euclid(m, c % m)
n = (min + gcd - 1) // gcd
candidate = n * gcd
if candidate > max:
return None
return (n * d) % m
The function extended_euclid(a, b) assumes that a and b are positive integers with a > b. It returns a triple (gcd, p, q), where gcd = p * a + q * b. So if we call it as gcd, _, d = extended_euclid(m, c % m) (the modulo operator is just to handle the case where c > m), we'll have gcd == (c * d) % m.
I have written about how the extended Euclidean algorithm works before so I won't repeat myself here, but here is an implementation:
def extended_euclid(a, b,
p_a = 1, q_a = 0, p_b = 0, q_b = 1):
if b == 0:
return a, p_a, q_a
n = a // b
return extended_euclid(b, a - n * b,
p_b, q_b, p_a - n * p_b, q_a - n * q_b)
Or, alternatively, a version that uses iteration instead of recursion:
def extended_euclid(a, b,
p_a = 1, q_a = 0, p_b = 0, q_b = 1):
while b > 0:
n = a // b
a, b, p_a, q_a, p_b, q_b = (b, a - n * b,
p_b, q_b, p_a - n * p_b, q_a - n * q_b)
return a, p_a, q_a