From 74628226c7f547be14e6f36bbd2384f30e346e5c Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Tue, 24 Dec 2019 22:36:04 -0500 Subject: [PATCH] Add day 22 part 2 solution --- day22/py/main.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/day22/py/main.py b/day22/py/main.py index 201ff97..b3f5c00 100644 --- a/day22/py/main.py +++ b/day22/py/main.py @@ -69,6 +69,49 @@ def part1(inputs: List[Operation]) -> int: return deck.index(2019) +def get_offset_and_increment_after_operations(inputs: List[Operation], deck_size: int) -> Tuple[int, int]: + # Most of this came from https://www.reddit.com/r/adventofcode/comments/ee0rqi/2019_day_22_solutions/fbnkaju/ + # Many thanks to /u/mcpower_ for this :) + # Offset represents the first item in our deck, and increment represents the difference to the next item + # (Modulating by deck_size) + offset = 0 + increment = 1 + for operation, arg in inputs: + if operation == Operation.DEAL_TO_STACK: + increment = (increment * -1) % deck_size + offset = (offset + increment) % deck_size + elif operation == Operation.CUT: + offset = (offset + increment * arg) % deck_size + elif operation == Operation.DEAL_BY_N: + # Calculate the modulcar inverse with Fermat's Little Thereom. + # We know that arg^(deck_size - 1) = 1 (mod deck_size), and thus + # arg^(deck_size - 2) = (deck_size)^(-1) (mod deck_size) + modular_inverse = pow(arg, deck_size - 2, deck_size) + # We want to find the element such that increment * arg = 1 (i.e. in the second position), + # which, by definition, is the modular inverse. second - first = offset, and this + # works out to increment * modular_inverse. + increment = (increment * modular_inverse) % deck_size + + return (offset, increment) + + +def part2(inputs: List[Operation]) -> int: + DECK_SIZE = 119315717514047 + NUM_SHUFFLES = 101741582076661 + offset_after_shuffle, increment_after_shuffle = get_offset_and_increment_after_operations(inputs, DECK_SIZE) + # We know that increment = increment * k_1 and offset = offset + increment * k_2. + # This can be expanded to a geometric sequence such that offset = k_2 * (1 - (k_1)^n)/(1 - k_1) after n shuffles + # and increment = increment * k_1^n, and since increment = 1, increment = k_1^n. + # We define k_1 = increment_after_shuffle and k_2 = offset_after_shuffle + a = pow(increment_after_shuffle, NUM_SHUFFLES, DECK_SIZE) + + # Apply the geometric series formula, performing division by dividing by the modular inverse. + b = offset_after_shuffle * (1 - a) * pow(1 - increment_after_shuffle, DECK_SIZE - 2, DECK_SIZE) + + # Get the card in the 2020th position. + return (a * 2020 + b) % DECK_SIZE + + if __name__ == '__main__': if len(sys.argv) != 2: print("Usage: ./main.py in_file") @@ -78,3 +121,4 @@ if __name__ == '__main__': inputs = [parse_command(line.rstrip('\n')) for line in f] print(part1(inputs)) + print(part2(inputs))