Reduce the complexity of other/graham_scan.py (#7953)

* Reduce the complexity of other/graham_scan.py

* Lower the --max-complexity threshold in the file .flake8

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix tests

* Update other/graham_scan.py

Co-authored-by: Christian Clauss <cclauss@me.com>

* Update graham_scan.py

* Update other/graham_scan.py

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Christian Clauss <cclauss@me.com>
This commit is contained in:
Maxim Smolskiy 2022-11-03 01:56:30 +03:00 committed by GitHub
parent a02de964d1
commit 978414bd50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 75 deletions

View File

@ -1,7 +1,7 @@
[flake8]
max-line-length = 88
# max-complexity should be 10
max-complexity = 20
max-complexity = 19
extend-ignore =
# Formatting style for `black`
E203 # Whitespace before ':'

View File

@ -14,6 +14,82 @@ from math import atan2, degrees
from sys import maxsize
# traversal from the lowest and the most left point in anti-clockwise direction
# if direction gets right, the previous point is not the convex hull.
class Direction(Enum):
left = 1
straight = 2
right = 3
def __repr__(self):
return f"{self.__class__.__name__}.{self.name}"
def angle_comparer(point: tuple[int, int], minx: int, miny: int) -> float:
"""Return the angle toward to point from (minx, miny)
:param point: The target point
minx: The starting point's x
miny: The starting point's y
:return: the angle
Examples:
>>> angle_comparer((1,1), 0, 0)
45.0
>>> angle_comparer((100,1), 10, 10)
-5.710593137499642
>>> angle_comparer((5,5), 2, 3)
33.690067525979785
"""
# sort the points accorgind to the angle from the lowest and the most left point
x, y = point
return degrees(atan2(y - miny, x - minx))
def check_direction(
starting: tuple[int, int], via: tuple[int, int], target: tuple[int, int]
) -> Direction:
"""Return the direction toward to the line from via to target from starting
:param starting: The starting point
via: The via point
target: The target point
:return: the Direction
Examples:
>>> check_direction((1,1), (2,2), (3,3))
Direction.straight
>>> check_direction((60,1), (-50,199), (30,2))
Direction.left
>>> check_direction((0,0), (5,5), (10,0))
Direction.right
"""
x0, y0 = starting
x1, y1 = via
x2, y2 = target
via_angle = degrees(atan2(y1 - y0, x1 - x0))
via_angle %= 360
target_angle = degrees(atan2(y2 - y0, x2 - x0))
target_angle %= 360
# t-
# \ \
# \ v
# \|
# s
# via_angle is always lower than target_angle, if direction is left.
# If they are same, it means they are on a same line of convex hull.
if target_angle > via_angle:
return Direction.left
elif target_angle == via_angle:
return Direction.straight
else:
return Direction.right
def graham_scan(points: list[tuple[int, int]]) -> list[tuple[int, int]]:
"""Pure implementation of graham scan algorithm in Python
@ -57,86 +133,12 @@ def graham_scan(points: list[tuple[int, int]]) -> list[tuple[int, int]]:
# remove the lowest and the most left point from points for preparing for sort
points.pop(minidx)
def angle_comparer(point: tuple[int, int], minx: int, miny: int) -> float:
"""Return the angle toward to point from (minx, miny)
:param point: The target point
minx: The starting point's x
miny: The starting point's y
:return: the angle
Examples:
>>> angle_comparer((1,1), 0, 0)
45.0
>>> angle_comparer((100,1), 10, 10)
-5.710593137499642
>>> angle_comparer((5,5), 2, 3)
33.690067525979785
"""
# sort the points accorgind to the angle from the lowest and the most left point
x = point[0]
y = point[1]
angle = degrees(atan2(y - miny, x - minx))
return angle
sorted_points = sorted(points, key=lambda point: angle_comparer(point, minx, miny))
# This insert actually costs complexity,
# and you should instead add (minx, miny) into stack later.
# I'm using insert just for easy understanding.
sorted_points.insert(0, (minx, miny))
# traversal from the lowest and the most left point in anti-clockwise direction
# if direction gets right, the previous point is not the convex hull.
class Direction(Enum):
left = 1
straight = 2
right = 3
def check_direction(
starting: tuple[int, int], via: tuple[int, int], target: tuple[int, int]
) -> Direction:
"""Return the direction toward to the line from via to target from starting
:param starting: The starting point
via: The via point
target: The target point
:return: the Direction
Examples:
>>> check_direction((1,1), (2,2), (3,3))
Direction.straight
>>> check_direction((60,1), (-50,199), (30,2))
Direction.left
>>> check_direction((0,0), (5,5), (10,0))
Direction.right
"""
x0, y0 = starting
x1, y1 = via
x2, y2 = target
via_angle = degrees(atan2(y1 - y0, x1 - x0))
if via_angle < 0:
via_angle += 360
target_angle = degrees(atan2(y2 - y0, x2 - x0))
if target_angle < 0:
target_angle += 360
# t-
# \ \
# \ v
# \|
# s
# via_angle is always lower than target_angle, if direction is left.
# If they are same, it means they are on a same line of convex hull.
if target_angle > via_angle:
return Direction.left
elif target_angle == via_angle:
return Direction.straight
else:
return Direction.right
stack: deque[tuple[int, int]] = deque()
stack.append(sorted_points[0])
stack.append(sorted_points[1])