Speed improvements and time measurement

This commit is contained in:
Christoph Stahl 2023-12-12 12:11:06 +01:00
parent 0f233f8324
commit 8197d2a3df
3 changed files with 54 additions and 41 deletions

View file

@ -4,6 +4,7 @@ from typing import Any, Generic, Protocol, TypeVar
from urllib.request import Request, urlopen from urllib.request import Request, urlopen
from html.parser import HTMLParser from html.parser import HTMLParser
from importlib import import_module from importlib import import_module
from timeit import default_timer
T1 = TypeVar("T1") T1 = TypeVar("T1")
T2 = TypeVar("T2") T2 = TypeVar("T2")
@ -187,7 +188,10 @@ def run_latest() -> None:
] ]
day = days[-1] day = days[-1]
print(f"Running {day.__name__}") print(f"Running {day.__name__}")
s_time = default_timer()
day.main() day.main()
e_time = default_timer()
print(f"Time: {e_time-s_time}")
def main() -> None: def main() -> None:
@ -196,10 +200,16 @@ def main() -> None:
for file in sorted(Path("aoc").iterdir()) for file in sorted(Path("aoc").iterdir())
if file.name.endswith(".py") and file.name.startswith("day") if file.name.endswith(".py") and file.name.startswith("day")
] ]
b_time = default_timer()
for day in days: for day in days:
print(f"Running {day.__name__}") print(f"Running {day.__name__}")
s_time = default_timer()
day.main() day.main()
e_time = default_timer()
print(f"Time: {e_time-s_time}")
print() print()
f_time = default_timer()
print(f"Overall time: {f_time - b_time}")
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -44,21 +44,19 @@ class Day08(AocSameParser[Map], Aoc2[Map, Map]):
def part2(self, inpt: Map) -> int: def part2(self, inpt: Map) -> int:
current = [node for node in inpt.rules.keys() if node.endswith("A")] current = [node for node in inpt.rules.keys() if node.endswith("A")]
steps_to_z: list[list[int]] = [[] for _ in current] steps_to_z = [0] * len(current)
loops: list[Optional[tuple[int, int]]] = [None for _ in current] for direction in itertools.cycle(inpt.directions):
dirty = False
for steps, direction in enumerate(itertools.cycle(inpt.directions)):
for n, node in enumerate(current): for n, node in enumerate(current):
if node.endswith("Z"): if node.endswith("Z"):
steps_to_z[n].append(steps) continue
loops[n] = self.find_loop(steps_to_z[n]) dirty = True
steps_to_z[n] += 1
if not any(loop is None for loop in loops): current[n] = inpt.rules[node][direction]
if not dirty:
break break
current = [inpt.rules[node][direction] for node in current]
# Turns out, each path directly loops :/ return math.lcm(*steps_to_z)
return math.lcm(*(loop[0] for loop in loops if loop is not None))
def main() -> None: def main() -> None:

View file

@ -1,8 +1,9 @@
from __future__ import annotations from __future__ import annotations
from array import array from array import array
from collections.abc import Iterable from collections.abc import Callable, Iterable
from dataclasses import dataclass from dataclasses import dataclass
from itertools import accumulate, combinations
from .aoc import Aoc2, AocSameParser from .aoc import Aoc2, AocSameParser
@ -64,43 +65,50 @@ class Matrix:
return "\n".join((x.tounicode() for x in self.iter_rows())) return "\n".join((x.tounicode() for x in self.iter_rows()))
def finditer(self, element: str) -> Iterable[Position]: def finditer(self, element: str) -> Iterable[Position]:
for y, line in enumerate(self.iter_rows()): return (
for x, char in enumerate(line): Position(x, y)
if char == element: for y, line in enumerate(self.iter_rows())
yield Position(x, y) for x, char in enumerate(line)
if char == element
)
def filter_row_ids(self, predicate: Callable[[array[str]], bool]) -> Iterable[int]:
return (y for y, row in enumerate(self.iter_rows()) if predicate(row))
def filter_col_ids(self, predicate: Callable[[array[str]], bool]) -> Iterable[int]:
return (x for x, col in enumerate(self.iter_cols()) if predicate(col))
@dataclass @dataclass
class Map: class Map:
map: Matrix map: Matrix
galaxies: list[Position] galaxies: Iterable[tuple[Position, Position]]
adjusted_galaxies: list[Position]
large_adjusted_galaxies: list[Position]
@classmethod @classmethod
def from_input(cls, inpt: str) -> Map: def from_input(cls, inpt: str) -> Map:
matrix = Matrix(inpt) matrix = Matrix(inpt)
missing_xs = array(
galaxies = list(matrix.finditer("#")) "I",
missing_ys = [ accumulate([1 if col.count("#") == 0 else 0 for col in matrix.iter_cols()]),
y for y, row in enumerate(matrix.iter_rows()) if row.count("#") == 0
]
missing_xs = [
x for x, col in enumerate(matrix.iter_cols()) if col.count("#") == 0
]
adjusted_galaxies = []
large_adjusted_galaxies = []
for pos in galaxies:
offset = Position(
len([m for m in missing_xs if m < pos.x]),
len([m for m in missing_ys if m < pos.y]),
) )
adjusted_galaxies.append(pos + offset)
large_adjusted_galaxies.append(pos + 999_999 * offset)
return cls(matrix, galaxies, adjusted_galaxies, large_adjusted_galaxies) missing_ys = array(
"I",
accumulate([1 if row.count("#") == 0 else 0 for row in matrix.iter_rows()]),
)
offset_galaxies = (
(
Position(
missing_xs[pos.x],
missing_ys[pos.y],
),
pos,
)
for pos in matrix.finditer("#")
)
return cls(matrix, offset_galaxies)
class Day11(AocSameParser[Map], Aoc2[Map, Map]): class Day11(AocSameParser[Map], Aoc2[Map, Map]):
@ -109,16 +117,13 @@ class Day11(AocSameParser[Map], Aoc2[Map, Map]):
def part1(self, inpt: Map) -> int: def part1(self, inpt: Map) -> int:
return sum( return sum(
g1 | g2 g1 | g2 for g1, g2 in combinations(((o + g) for o, g in inpt.galaxies), 2)
for i, g1 in enumerate(inpt.adjusted_galaxies)
for g2 in inpt.adjusted_galaxies[i + 1 :]
) )
def part2(self, inpt: Map) -> int: def part2(self, inpt: Map) -> int:
return sum( return sum(
g1 | g2 g1 | g2
for i, g1 in enumerate(inpt.large_adjusted_galaxies) for g1, g2 in combinations(((999_999 * o + g) for o, g in inpt.galaxies), 2)
for g2 in inpt.large_adjusted_galaxies[i + 1 :]
) )