mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-23 21:11:08 +00:00
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:
parent
a02de964d1
commit
978414bd50
2
.flake8
2
.flake8
|
@ -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 ':'
|
||||
|
|
|
@ -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])
|
||||
|
|
Loading…
Reference in New Issue
Block a user