Add day 22 part 2 solution

master
Nick Krichevsky 2019-12-24 22:36:04 -05:00
parent 1241aa2d35
commit 74628226c7
1 changed files with 44 additions and 0 deletions

View File

@ -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))