2016: d15: ex1: add solution

This commit is contained in:
Bruno BELANYI 2025-05-19 20:40:15 +01:00
parent 3d4c9cb443
commit 5939ba8fd1

61
2016/d15/ex1/ex1.py Executable file
View file

@ -0,0 +1,61 @@
#!/usr/bin/env python
import math
import sys
from typing import NamedTuple
class Disc(NamedTuple):
num: int
positions: int
start_offset: int
@classmethod
def from_str(cls, input: str) -> "Disc":
input = input.removeprefix("Disc #").removesuffix(".")
split_input = input.split()
return Disc(
int(split_input[0]),
int(split_input[2]),
int(split_input[-1]),
)
def solve(input: str) -> int:
def parse(input: str) -> list[Disc]:
return [Disc.from_str(line) for line in input.splitlines()]
# https://rosettacode.org/wiki/Chinese_remainder_theorem#Python_3.6
def mul_inv(a: int, b: int) -> int:
b0 = b
x0, x1 = 0, 1
if b == 1:
return 1
while a > 1:
q = a // b
a, b = b, a % b
x0, x1 = x1 - q * x0, x0
if x1 < 0:
x1 += b0
return x1
def chinese_remainder(residue_mapping: dict[int, int]) -> int:
res = 0
prod = math.prod(residue_mapping)
for n_i, a_i in residue_mapping.items():
p = prod // n_i
res += a_i * mul_inv(p, n_i) * p
return res % prod
discs = parse(input)
residues = {disc.positions: -(disc.start_offset + disc.num) for disc in discs}
return chinese_remainder(residues)
def main() -> None:
input = sys.stdin.read()
print(solve(input))
if __name__ == "__main__":
main()