perf: optimize solution 2025/09

This commit is contained in:
2025-12-15 23:53:58 +01:00
parent 0000025016
commit 0000026012

View File

@@ -1,4 +1,3 @@
from collections.abc import Generator
from typing import Any from typing import Any
Point = tuple[int, int] Point = tuple[int, int]
@@ -14,10 +13,7 @@ def _parse_input(input_data) -> list[Point]:
def _rectangle_size(p1: Point, p2: Point) -> int: def _rectangle_size(p1: Point, p2: Point) -> int:
x1, y1 = p1 return (abs(p1[0] - p2[0]) + 1) * (abs(p1[1] - p2[1]) + 1)
x2, y2 = p2
return (abs(x1 - x2) + 1) * (abs(y1 - y2) + 1)
def part_1(input_data: str) -> Any: def part_1(input_data: str) -> Any:
@@ -47,22 +43,8 @@ def _get_dir(p1: Point, p2: Point) -> Point:
return dx, dy return dx, dy
def _get_segment(p1: Point, p2: Point) -> Generator[Point]:
dx, dy = _get_dir(p1, p2)
points = []
sx, sy = p1
while (sx, sy) != p2:
yield sx, sy
points.append((sx, sy))
sx, sy = sx + dx, sy + dy
yield p2
def _compress_coordinates(points: list[Point], y: bool) -> dict[int, int]: def _compress_coordinates(points: list[Point], y: bool) -> dict[int, int]:
return {v: k for k, v in enumerate(sorted({p[y] for p in points}))} return {v: k for k, v in enumerate(sorted({p[y] for p in points}), 1)}
def _find_inner( def _find_inner(
@@ -80,15 +62,17 @@ def _find_inner(
c1, c2 = c(p1), c(p2) c1, c2 = c(p1), c(p2)
rng = _get_segment(c1, c2) x, y = c1
dx, dy = _get_dir(c1, c2) dx, dy = _get_dir(c1, c2)
# rotate 90 positive as inside is in the positive direction pdx, pdy = -dy, dx
dx, dy = -dy, dx
for x, y in rng: while (x, y) != c2:
inner.add((x, y)) inner.add((x, y))
inside.add((x + dx, y + dy)) inside.add((x + pdx, y + pdy))
x, y = x + dx, y + dy
inner.add((x, y))
inside.add((x + pdx, y + pdy))
stack = list(inside - inner) stack = list(inside - inner)
@@ -106,18 +90,6 @@ def _find_inner(
return inner return inner
def _get_corner_points(p1: Point, p2: Point) -> tuple[Point, ...]:
if p1 == p2:
return (p1,)
px1, py1 = p1
px2, py2 = p2
p3 = px1, py2
p4 = px2, py1
return p1, p3, p2, p4
def part_2(input_data: str) -> Any: def part_2(input_data: str) -> Any:
points = _parse_input(input_data) points = _parse_input(input_data)
@@ -129,6 +101,19 @@ def part_2(input_data: str) -> Any:
inner = _find_inner(points, x_compression, y_compression) inner = _find_inner(points, x_compression, y_compression)
w = max(x_compression.values())
h = max(y_compression.values())
prefix = [[0] * (h + 1) for _ in range(w + 1)]
for x in range(1, w + 1):
for y in range(1, h + 1):
prefix[x][y] = (
((x, y) in inner)
+ prefix[x - 1][y]
+ prefix[x][y - 1]
- prefix[x - 1][y - 1]
)
m = None m = None
for idx, p1 in enumerate(points): for idx, p1 in enumerate(points):
@@ -136,19 +121,28 @@ def part_2(input_data: str) -> Any:
for p2 in points[idx + 1 :]: for p2 in points[idx + 1 :]:
c2 = c(p2) c2 = c(p2)
c1, c2, c3, c4 = _get_corner_points(c1, c2) size = _rectangle_size(p1, p2)
if m and size <= m:
continue
is_inside = ( min_x = min(c1[0], c2[0])
all(x in inner for x in _get_segment(c1, c2)) max_x = max(c1[0], c2[0])
and all(x in inner for x in _get_segment(c2, c3)) min_y = min(c1[1], c2[1])
and all(x in inner for x in _get_segment(c3, c4)) max_y = max(c1[1], c2[1])
and all(x in inner for x in _get_segment(c4, c1))
c_expected_size = _rectangle_size(c1, c2)
c_size = (
prefix[max_x][max_y]
- prefix[min_x - 1][max_y]
- prefix[max_x][min_y - 1]
+ prefix[min_x - 1][min_y - 1]
) )
is_inside = c_size == c_expected_size
if not is_inside: if not is_inside:
continue continue
size = _rectangle_size(p1, p2)
m = max(m or size, size) m = max(m or size, size)
return m return m