12_jupiter.py 1.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. #!/usr/bin/env python3
  2. from itertools import chain, combinations, islice
  3. from math import gcd
  4. def cmp(a, b):
  5. return (a > b) - (a < b)
  6. def abssum(i):
  7. return sum(map(abs, i))
  8. def lcm(a, b):
  9. return abs(a * b) // gcd(a, b)
  10. def sim(axis):
  11. pos = list(axis)
  12. vel = [0] * len(pos)
  13. index_pairs = tuple(combinations(range(len(pos)), 2))
  14. while True:
  15. for i, j in index_pairs:
  16. diff = cmp(pos[j], pos[i])
  17. vel[i] += diff
  18. vel[j] -= diff
  19. for i, v in enumerate(vel):
  20. pos[i] += v
  21. yield pos, vel
  22. def energy_after(moons, steps):
  23. sims = zip(*map(sim, zip(*moons)))
  24. pos, vel = zip(*next(islice(sims, steps - 1, steps)))
  25. return sum(abssum(p) * abssum(v) for p, v in zip(zip(*pos), zip(*vel)))
  26. def axis_cycle(axis):
  27. seen = {}
  28. for step, (pos, vel) in enumerate(sim(axis)):
  29. prevstep = seen.setdefault(tuple(pos + vel), step)
  30. if prevstep != step:
  31. return step - prevstep
  32. def find_cycle(moons):
  33. x, y, z = zip(*moons)
  34. return lcm(lcm(axis_cycle(x), axis_cycle(y)), axis_cycle(z))
  35. moons = [(3, 15, 8), (5, -1, -2), (-10, 8, 2), (8, 4, -5)]
  36. print(energy_after(moons, 1000))
  37. print(find_cycle(moons))