From 00000120e2499aa09cf5a323f3dc1a3b291591ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Jane=C5=BEi=C4=8D?= Date: Fri, 5 Dec 2025 20:34:20 +0100 Subject: [PATCH] perf: optimize solution 2025/05 --- src/solution/year_2025/day_05.py | 101 +++++++------------------------ 1 file changed, 21 insertions(+), 80 deletions(-) diff --git a/src/solution/year_2025/day_05.py b/src/solution/year_2025/day_05.py index 9b8965f..5874e99 100644 --- a/src/solution/year_2025/day_05.py +++ b/src/solution/year_2025/day_05.py @@ -1,48 +1,6 @@ -from typing import Any, Self +from typing import Any, TypeAlias - -class Range: - def __init__(self, start: int, end: int) -> None: - self.start = start - self.end = end - - def __str__(self) -> str: - return f"{self.start}..={self.end}" - - def __repr__(self) -> str: - return f"Range({self.start}, {self.end})" - - def __len__(self) -> int: - return max(self.end - self.start + 1, 0) - - def __eq__(self, value: object, /) -> bool: - if not isinstance(value, Range): - return False - return self.start == value.start and self.end == value.end - - def __contains__(self, value: int) -> bool: - return self.start <= value <= self.end - - def discard(self, other: Self) -> list[Self]: - r1, r2 = self.start, self.end - d1, d2 = other.start, other.end - - res = [] - - if r1 < d1: - t = min(r2, d1 - 1) - res.append(Range(r1, t)) - - if r1 < d1 and r2 > d2: - res.append(Range(d2 + 1, r2)) - - if d1 <= r1 <= d2: - res.append(Range(d2 + 1, r2)) - - if r1 > d2: - res.append(self) - - return list(filter(len, res)) +Range: TypeAlias = tuple[int, int] def _parse_input(input_data) -> tuple[list[Range], list[int]]: @@ -51,7 +9,7 @@ def _parse_input(input_data) -> tuple[list[Range], list[int]]: ranges = [] for r in _ranges.splitlines(): a, b = r.split("-") - ranges.append(Range(int(a), int(b))) + ranges.append((int(a), int(b))) ids = list(map(int, _ids.splitlines())) @@ -63,8 +21,8 @@ def part_1(input_data: str) -> Any: c = 0 for i in ids: - for r in ranges: - if i in r: + for r1, r2 in ranges: + if r1 <= i <= r2: c += 1 break @@ -73,45 +31,28 @@ def part_1(input_data: str) -> Any: def part_2(input_data: str) -> Any: ranges, _ = _parse_input(input_data) - res: list[Range] = [] - for i, r1 in enumerate(ranges): - acc = [r1] + points: list[tuple[int, int]] = [] + for i, (r1, r2) in enumerate(ranges): + points.extend(((r1, i), (r2, i))) - for r2 in ranges[i + 1 :]: - nacc = [] - for r3 in acc: - nacc.extend(r3.discard(r2)) - acc = nacc - res.extend(acc) + points.sort() - return sum(map(len, res)) + c = 0 + opened = set() + pp = points[0][0] - 1 + for p, pid in points: + o = pid not in opened + opened.add(pid) if o else opened.remove(pid) + if not o or len(opened) > 1: + c += p - pp + elif p != pp: + c += 1 -def test_range(): - r1 = Range(1, 3) - r2 = Range(1, 0) + pp = p - # len - assert len(r1) == 3 - assert len(r2) == 0 - - # contains - assert 0 not in r1 - assert 1 in r1 - assert 2 in r1 - assert 3 in r1 - assert 4 not in r1 - - # eq - assert Range(2, 2) == Range(2, 2) - assert Range(300, 1234) == Range(300, 1234) - assert Range(2, 3) != Range(2, 2) - - # discard - r3 = Range(2, 2) - assert r1.discard(r2) == [r1] - assert r1.discard(r3) == [Range(1, 1), Range(3, 3)] + return c def test_part_1(example_data):