···11import re
22import fileinput
33-from utils import parse_line
33+from utils import parse_line, mul
445566def button_timing(discs):
···20202121 i += stride
22222323+# https://stackoverflow.com/a/9758173/239076
2424+def egcd(a, b):
2525+ if a == 0:
2626+ return (b, 0, 1)
2727+ else:
2828+ g, y, x = egcd(b % a, a)
2929+ return (g, x - (b // a) * y, y)
3030+3131+def modinv(a, m):
3232+ g, x, y = egcd(a, m)
3333+ if g != 1:
3434+ raise Exception('modular inverse does not exist')
3535+ else:
3636+ return x % m
3737+3838+def chinese_remainder_theorem(discs):
3939+ M = mul(d[0] for d in discs)
4040+ x = 0
4141+4242+ for i, (size, initial) in enumerate(discs, start=1):
4343+ # Disc #2 has 17 positions; at time=0, it is at position 15.
4444+ # => x \equiv (17 - 15 - 2) (mod 17)
4545+ M_i = (M / size)
4646+ x += (size - initial - i) * M_i * modinv(M_i, size)
4747+4848+ return x
4949+23502451DISCS = []
2552DISC_RE = re.compile(r'Disc #\d+ has (\d+) positions; at time=0, it is at position (\d+).')
···2855 disc = parse_line(DISC_RE, line.strip())
2956 DISCS.append(disc)
30573131-print "Timing to press button:", button_timing(DISCS)
3232-print "Timing with added disc:", button_timing(DISCS + [[11, 0]])
5858+# print "Timing to press button:", button_timing(DISCS)
5959+# print "Timing with added disc:", button_timing(DISCS + [[11, 0]])
6060+print "Timing to press button:", chinese_remainder_theorem(DISCS)
6161+print "Timing with added disc:", chinese_remainder_theorem(DISCS + [[11, 0]])