Advertisement
hhoppe

Advent of code 2020 day 13

Dec 13th, 2020 (edited)
170
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.06 KB | None | 0 0
  1. # Part 1
  2. def earliest_bus(s):
  3.   time = int(s.split('\n')[0])
  4.   buses = [int(e) for e in s.split('\n')[1].split(',') if e != 'x']
  5.   next_times = [((time - 1) // bus + 1) * bus for bus in buses]
  6.   earliest_time, earliest_bus = min(zip(next_times, buses))
  7.   return (earliest_time - time) * earliest_bus
  8.  
  9. # Part 2
  10. def extended_gcd(a: int, b: int):
  11.   """Finds the greatest common divisor using the extended Euclidean algorithm.
  12.  
  13.  Returns the tuple (gcd(a, b), x, y) where 'x' and 'y' have the property that
  14.  a * x + b * y = gcd(a, b).
  15.  
  16.  For the case that a and b are coprime, i.e. gcd(a, b) = 1,
  17.  applying "modulo b" to both sides results in
  18.  (a * x) % b == 1,
  19.  and hence 'x' is the modular multiplicative inverse of 'a' with respect to
  20.  the modulus 'b'.
  21.  See https://en.wikipedia.org/wiki/Modular_multiplicative_inverse
  22.  """
  23.   prev_x, x = 1, 0
  24.   prev_y, y = 0, 1
  25.   while b:
  26.     q = a // b
  27.     x, prev_x = prev_x - q * x, x
  28.     y, prev_y = prev_y - q * y, y
  29.     a, b = b, a % b
  30.   x, y = prev_x, prev_y
  31.   return a, x, y
  32.  
  33. def modulo_inverse(a: int, b: int) -> int:
  34.   return extended_gcd(a, b)[1]
  35.  
  36. # Given [(b_i, r_i)], where b_i are coprime, we want to find x such that
  37. #  x % b_i = r_i  for all i,  (where initial r_i = -i % b_i)
  38. # We can work by reduction, merging pairs of buses.
  39.  
  40. # Given a pair of buses (b1, r1) and (b2, r2),
  41. # we want to find a new equivalent bus (b1 * b2, r) such that
  42. #  0 <= r < b1 * b2
  43. #  r % b1 = r1
  44. #  r % b2 = r2
  45.  
  46. # Let r = k * b1 + r1  for some unknown k.
  47. # Then, (k * b1 + r1) % b2 = r2
  48. # Therefore, k = inv(b1, b2) * (r2 - r1 % b2)
  49. #  where inv(b1, b2) is the multiplicative inverse of b1 wrt to modulus b2.
  50.  
  51. def earliest_consecutive(s):
  52.   buses = [int(e) for e in s.replace('x', '1').split(',')]
  53.  
  54.   def merge_buses(bus1, bus2):
  55.     (b1, r1), (b2, r2) = bus1, bus2
  56.     k = modulo_inverse(b1, b2) * (r2 - r1 % b2)
  57.     r = (k * b1 + r1) % (b1 * b2)
  58.     return b1 * b2, r
  59.  
  60.   bus_remainders = [(bus, -i % bus) for i, bus in enumerate(buses) if bus > 1]
  61.   _, r = functools.reduce(merge_buses, bus_remainders)
  62.   return r
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement