Skip to content

Commit bda7b7b

Browse files
author
Simon Lammer
committed
Implement the melkman anlgorithm for computing convex hulls
1 parent f36a2f6 commit bda7b7b

File tree

1 file changed

+71
-1
lines changed

1 file changed

+71
-1
lines changed

Diff for: divide_and_conquer/convex_hull.py

+71-1
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,72 @@ def _construct_hull(points, left, right, convex_set):
411411
_construct_hull(candidate_points, extreme_point, right, convex_set)
412412

413413

414+
def convex_hull_melkman(points):
415+
"""
416+
Constructs the convex hull of a set of 2D points using the melkman algorithm.
417+
The algorithm works by iteratively inserting points of a simple polygonal chain
418+
(meaning that no line segments between two consecutive points cross each other).
419+
Sorting the points yields such a polygonal chain.
420+
421+
Runtime: O(n log n) - O(n) if points are already sorted in the input
422+
423+
Parameters
424+
---------
425+
points: array-like of object of Points, lists or tuples.
426+
The set of 2d points for which the convex-hull is needed
427+
428+
Returns
429+
------
430+
convex_set: list, the convex-hull of points sorted in non-decreasing order.
431+
432+
See Also
433+
--------
434+
435+
Examples
436+
---------
437+
>>> convex_hull_melkman([[0, 0], [1, 0], [10, 1]])
438+
[(0.0, 0.0), (1.0, 0.0), (10.0, 1.0)]
439+
>>> convex_hull_melkman([[0, 0], [1, 0], [10, 0]])
440+
[(0.0, 0.0), (10.0, 0.0)]
441+
>>> convex_hull_melkman([[-1, 1],[-1, -1], [0, 0], [0.5, 0.5], [1, -1], [1, 1],
442+
... [-0.75, 1]])
443+
[(-1.0, -1.0), (-1.0, 1.0), (1.0, -1.0), (1.0, 1.0)]
444+
>>> convex_hull_melkman([(0, 3), (2, 2), (1, 1), (2, 1), (3, 0), (0, 0), (3, 3),
445+
... (2, -1), (2, -4), (1, -3)])
446+
[(0.0, 0.0), (0.0, 3.0), (1.0, -3.0), (2.0, -4.0), (3.0, 0.0), (3.0, 3.0)]
447+
"""
448+
points = sorted(_validate_input(points))
449+
n = len(points)
450+
451+
convex_hull = points[:2]
452+
for i in range(2, n):
453+
det = _det(convex_hull[1], convex_hull[0], points[i])
454+
if det > 0:
455+
convex_hull.insert(0, points[i])
456+
break
457+
elif det < 0:
458+
convex_hull.append(points[i])
459+
break
460+
else:
461+
convex_hull[1] = points[i]
462+
i += 1
463+
464+
for i in range(i, n):
465+
if (_det(convex_hull[0], convex_hull[-1], points[i]) > 0
466+
and _det(convex_hull[-1], convex_hull[0], points[1]) < 0):
467+
# The point lies within the convex hull
468+
continue
469+
470+
convex_hull.insert(0, points[i])
471+
convex_hull.append(points[i])
472+
while _det(convex_hull[0], convex_hull[1], convex_hull[2]) >= 0:
473+
del convex_hull[1]
474+
while _det(convex_hull[-1], convex_hull[-2], convex_hull[-3]) <= 0:
475+
del convex_hull[-2]
476+
477+
# `convex_hull` is contains the convex hull in circular order
478+
return sorted(convex_hull[1:] if len(convex_hull) > 3 else convex_hull)
479+
414480
def main():
415481
points = [
416482
(0, 3),
@@ -426,10 +492,14 @@ def main():
426492
]
427493
# the convex set of points is
428494
# [(0, 0), (0, 3), (1, -3), (2, -4), (3, 0), (3, 3)]
429-
results_recursive = convex_hull_recursive(points)
430495
results_bf = convex_hull_bf(points)
496+
497+
results_recursive = convex_hull_recursive(points)
431498
assert results_bf == results_recursive
432499

500+
results_melkman = convex_hull_melkman(points)
501+
assert results_bf == results_melkman
502+
433503
print(results_bf)
434504

435505

0 commit comments

Comments
 (0)