-
-
Save pjt33/c942040a9b8fa9bacc7f5d1bccc7ac2b to your computer and use it in GitHub Desktop.
#!/usr/bin/pypy3 | |
from heapq import heappush, heappop | |
def mod_inv(a: int, m: int) -> int: | |
lastremainder, remainder = a % m, m | |
x, lastx, y, lasty = 0, 1, 1, 0 | |
while remainder: | |
lastremainder, (quotient, remainder) = remainder, divmod(lastremainder, remainder) | |
x, lastx = lastx - quotient*x, x | |
y, lasty = lasty - quotient*y, y | |
if lastremainder != 1: | |
raise ValueError | |
return lastx % m | |
def chinese_remainder(residues) -> int: | |
total = 0 | |
M = 1 | |
for m in residues: | |
M *= m | |
for m, a in residues.items(): | |
p = M // m | |
total += a * mod_inv(p, m) * p | |
return total % M | |
def eval_poly(ps, x: int) -> int: | |
y = 0 | |
for c in ps[-1::-1]: | |
y = x * y + c | |
return y | |
def poly_search(points, eval_cost, stop_on_optimal = True, max_degree = None, max_abs_coeff = None): | |
""" | |
Search for the integer polynomial with smallest cost which interpolates the points. | |
Assume that the x-values are coprime. | |
Assume that the cost evaluation is monotonic in the absolute value of each coefficient. | |
points: A dict from x to p(x) | |
eval_cost: A function to evaluate the cost of a coefficient vector, passed as a tuple | |
stop_on_optimal (optional): set to False to keep producing solutions after finding all of the optimal ones | |
max_degree (optional): the maximum degree of polynomial to consider | |
max_abs_coeff (optional): the maximum absolute value of coefficients to consider | |
""" | |
max_degree = float('+inf') if max_degree is None else max_degree | |
max_abs_coeff = float('+inf') if max_abs_coeff is None else max_abs_coeff | |
lcm = 1 | |
for x in points: | |
lcm *= x | |
# (cost, len, coeffs in little-endian order, increment for the last one) | |
# len isn't strictly necessary, but ensures that infinite chains with fixed L^infty norm | |
# don't entirely suppress output. | |
q = [(0, 0, tuple(), None)] | |
push = lambda child, delta: heappush(q, (eval_cost(child), len(child), child, delta)) if abs(child[-1]) <= max_abs_coeff else None | |
best_cost = float('+inf') | |
while q: | |
cost, _, coeffs, delta = heappop(q) | |
if cost > best_cost and stop_on_optimal: | |
return | |
if len(coeffs) > max_degree + 1: | |
continue | |
if all(eval_poly(coeffs, x) == y for x, y in points.items()): | |
print(coeffs, "at cost", cost) | |
best_cost = cost | |
continue | |
if delta: | |
push(coeffs[:-1] + (coeffs[-1] + delta,), delta) | |
n = len(coeffs) | |
an = chinese_remainder({ x : (y - eval_poly(coeffs, x)) // x**n for x, y in points.items() }) | |
push(coeffs + (an,), lcm) | |
push(coeffs + (an - lcm,), -lcm) | |
print("sum of absolute values") | |
poly_search({3: 221157, 5: 31511625}, lambda coeffs: sum(abs(c) for c in coeffs)) | |
print() | |
print("maximum absolute value") | |
poly_search({3: 221157, 5: 31511625}, lambda coeffs: max(abs(c) for c in coeffs)) | |
print() | |
print("maximum absolute value: extra solutions at max abs coeff 8") | |
poly_search({3: 221157, 5: 31511625}, lambda coeffs: max(abs(c) for c in coeffs), False, max_degree = 24, max_abs_coeff = 8) | |
print() | |
print("maximum absolute value: extra solutions at degree 10") | |
poly_search({3: 221157, 5: 31511625}, lambda coeffs: max(abs(c) for c in coeffs), False, max_degree = 10, max_abs_coeff = 13) |
1
For [2:75776, 3: 24659883] I am getting error: an = chinese_remainder({ x : (y - eval_poly(coeffs, x)) // x**n for x, y in points.items() }) OverflowError: int too large to convert to float
There shouldn't be any floats involved at all. Are you using Python 2? If so, try it with Python 3. Otherwise try changing x**n
to pow(x, n)
. Note that with the maximum absolute value cost function this will loop infinitely unless you supply the max_degree
parameter, because it gets in a loop with (0, 0, 0, 0, 0, 0, 2, 1, -1, 2, -3, 2, -2, -2, 0, -1, -2, -2, -2, ...)
and (0, 0, 0, 0, 0, 0, 2, 1, -1, 2, 3, -3, -1, -2, 0, -1, -2, -2, -2, ...)
.
2
For dict_res = {2:1184, 3:33827 } It quickly finds FIRST polynom, but after waiting couple minutes I stopped it, because it was afraid of falling in infinite loop, can you please check it ? p^9+2p^8+3p^5+3p^4+2p^3-p^2+p+2
Yes, in this case it gets in a loop with (2, 1, -1, 2, -3, 2, -2, -2, 0, -1, -2, -2, -2, ...)
and (2, 1, -1, 2, 3, -3, -1, -2, 0, -1, -2, -2, -2, ...)
.
Thanks for reply ! No I am using Python 3.
May be problem that in my code 75776 , 24659883 were floats (actually integers) ? So example should be [2:75776.0, 3: 24659883.0] .
How that can be cured ? Any hint ?
May be problem that in my code 75776 , 24659883 were floats (actually integers) ?
Could be. There's a particular gotcha in Python that /
always gives a float. If you want integer division you have to use //
.
You can coerce a float to an integer as x = int(x)
.
Thanks again for very nice idea and code !
Here are some issues occured:
1
For [2:75776, 3: 24659883] I am getting error:
an = chinese_remainder({ x : (y - eval_poly(coeffs, x)) // x**n for x, y in points.items() })
OverflowError: int too large to convert to float
2
For dict_res = {2:1184, 3:33827 }
It quickly finds FIRST polynom, but after waiting couple minutes I stopped it, because it was afraid of falling in infinite loop,
can you please check it ?
p^9+2p^8+3p^5+3p^4+2p^3-p^2+p+2