perf: optimize solution 2025/09
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user