"""Solution to 2020/13 part2 Declarations: t: timestamp bus_id, bus: used as slope (a) in the linear function observation: bus_ids are distinct prime numbers offset: postion of the bus in the list t + offset = arrival of bus normalized_offset: used so f(0) is a correct (non-negative) solution = -offset % bus_id used as intercept (b) in linear function bus_solution: solutions for y in the scope of one bus. solution: smallest y that is a solution to all linear functions Functions: - The solutions per bus. linear (y=a*x+b) - t = bus_id * x + normalized_offset - x: natural numbers. xth solution (time of arrival - index) - y: solutions for t in the scope of one bus. t + delta = arrival of bus Combining two functions: - Given y = x0 * a0 + b0 - Given y = x1 * a1 + b1 - x0 * a0 + b0 = x1 * a1 + b1 - x1 = (x0 * a0 + b0 - b1) / a1 - Remember: x0 and x1 have to be whole numbers - Find linear function x0 = n * a + b that describes the possible solutions - Let x0 be in linear function x0 = n * a + b - x1 = ((n * a + b) * a0 + b0 - b1) / a1 - x1 = (n * a * a0 + a0 * b + b0 -b1) / a1 - x1 = (n * a * a0) / a1 + (a0 * b + b0 - b1) / a1 - For x1 to be a whole number: - (n * a * a0) needs to be divisible by a1 - since a's prime factors are distinct from a1 set a = a1 - a0 * b + b0 - b1 needs to be divisible by a1 - find b by brute force (b = magic_number) - => x0 = n * a1 + magic_number - Apply to y = x0 * a0 + b0 - y = (n * a1 + magic_number) * a0 + b0 - y = n * (a0 * a1) + (a0 + b0 + magic_number) - new a = a0 * a1 - new b = a0 + b0 + magic_number Apply this schema recursively (or iteratively) to all functions. Each interim result represents a formula for all possible solutions that includes the processed bus_ids. At last set n = 0 (in y = n * a + b) to receive the smallest solution (earliest timestamp). """ with open('input', 'r') as f: f.readline() buses = f.readline().strip().split(',') departures = [] i = 0 for offset, bus in enumerate(buses): try: bus = int(bus) normalized_offset = -offset % bus print(f'y = x{i} * {bus:>3} + {normalized_offset:>3} ({offset=:>2})') departures.append((bus, normalized_offset)) i += 1 except ValueError: pass print() a = 1 b = 0 for a_cur, b_cur in departures: magic_nr = None i = 0 while magic_nr is None: if (a * i + b - b_cur) % a_cur == 0: magic_nr = i i += 1 b = b + a * magic_nr a = a * a_cur print(f'y = n * {a} + {b} | {magic_nr=}') print() print('Solution:') print(b)