import aoclib import math DAY = 13 TEST_SOLUTION_PART1 = 295 TEST_SOLUTION_PART2 = 1068781 def part1(test_mode=False): my_input = aoclib.getInputAsArraySplit(day=DAY, split_char=",", test=test_mode) my_arrival = int(my_input[0][0]) bus_ids = [int(i) for i in my_input[1] if i != 'x'] waiting_times = [i - my_arrival % i for i in bus_ids] min_waiting_time = min(waiting_times) return min_waiting_time * bus_ids[waiting_times.index(min_waiting_time)] def part2(test_mode=False): my_input = aoclib.getInputAsArraySplit(day=DAY, split_char=",", test=test_mode) bus_ids = {i: int(v) for i, v in enumerate(my_input[1]) if v != 'x'} # utilizing the Chinese Remainder Theorem we are searching for x = i (mod bus_ids[i]) for all i # watch https://www.youtube.com/watch?v=ru7mWZJlRQg for an easy explanation # finding the "left" part of each (mod x) part base_multiplier = {i: math.prod(bus_ids.values()) // v for i, v in bus_ids.items()} # finding the "right" part of each (mod x) part utilizing the Extended Euclidean Algorithm # s. Python documentation on pow(x, -1, y) ext_multiplier = {i: pow(base_multiplier[i], -1, bus_ids[i]) for i in bus_ids} # sum all multiplications together and add our offset # EEA gives us base_multiplier[i] * x == 1(one!) (mod bus_ids[i]) # but we need base_multiplier[i] * x == i (mod bus_ids[i]) answer = sum(i * base_multiplier[i] * ext_multiplier[i] for i in bus_ids) # and shrink it down # for the -answer see pythons behaviour when calculating the mod of negative numbers with positive divisor # also: http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html lowest_common_multiple = math.lcm(*bus_ids.values()) answer = -answer % lowest_common_multiple return answer