22_spacecards.py 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. #!/usr/bin/env python3
  2. import sys
  3. from functools import partial, reduce
  4. # get (a, b) coefficients for inverse linear index mapping functions ax+b
  5. def inverse_functions(instructions, ncards):
  6. for line in reversed(instructions):
  7. words = line.split()
  8. if words[0] == 'cut':
  9. yield 1, int(words[1])
  10. elif words[1] == 'into':
  11. yield -1, ncards - 1
  12. else:
  13. increment = int(words[-1])
  14. index_increment = pow(increment, ncards - 2, ncards)
  15. yield index_increment, 0
  16. # compose linear functions ax+b and cx+d
  17. def compose(mod, f, g):
  18. a, b = f
  19. c, d = g
  20. return c * a % mod, (c * b + d) % mod
  21. # repeat ax+b n times
  22. def repeat_linear(f, n, mod):
  23. if n == 0:
  24. return 1, 0
  25. if n == 1:
  26. return f
  27. half, odd = divmod(n, 2)
  28. g = repeat_linear(f, half, mod)
  29. g = compose(mod, g, g)
  30. return compose(mod, f, g) if odd else g
  31. def card_at(index, ncards, nshuffles, instructions):
  32. functions = inverse_functions(instructions, ncards)
  33. invmap = reduce(partial(compose, ncards), functions, (1, 0))
  34. a, b = repeat_linear(invmap, nshuffles, ncards)
  35. return (index * a + b) % ncards
  36. instructions = list(sys.stdin)
  37. print(next(i for i in range(10007) if card_at(i, 10007, 1, instructions) == 2019))
  38. print(card_at(2020, 119315717514047, 101741582076661, instructions))