upd: fixed the error of farthestPointsDistance case. The final code is so verbose, whereas the result is definitely true.
You can implement your own functions closestPointsDistance
and farthestPointsDistance
.
farthestPointsDistance✅
Rotating Calipers Algorithm, $\text{average }\mathcal{O}(n \log n)$
Clear["Global`*"];
dist[p1_, p2_] := Norm[p2 - p1];
(*really naive, so slow sortPointsCounterClockwise*)
sortPointsCounterClockwise[pts_] :=
Module[{center = Mean[pts], angles},
angles = ArcTan @@ (# - center) & /@ pts;
pts[[Ordering[angles]]]];
convexHull[pts_] :=
sortPointsCounterClockwise@MeshCoordinates@ConvexHullMesh@pts;
TriangleArea[p1_, p2_, p3_] :=
Abs[(p1[[1]]*(p2[[2]] - p3[[2]]) + p2[[1]]*(p3[[2]] - p1[[2]]) +
p3[[1]]*(p1[[2]] - p2[[2]]))/2];
(*Proposed by Preparata and Shamos in 1985 that avoided calculation of angles *)
GetAllAntiPodalPairs[p_List] :=
Module[{n = Length[p], i0, i = 1, j, j0}, i0 = n;
j = i + 1;
While[TriangleArea[p[[i]], p[[Mod[i + 1, n, 1]]],
p[[Mod[j + 1, n, 1]]]] >
TriangleArea[p[[i]], p[[Mod[i + 1, n, 1]]], p[[Mod[j, n, 1]]]],
j = Mod[j + 1, n, 1]];
j0 = j;
Reap[While[i != j0, i = Mod[i + 1, n, 1];
Sow[{i, j}];
While[
TriangleArea[p[[i]], p[[Mod[i + 1, n, 1]]],
p[[Mod[j + 1, n, 1]]]] >
TriangleArea[p[[i]], p[[Mod[i + 1, n, 1]]], p[[Mod[j, n, 1]]]],
j = Mod[j + 1, n, 1];
If[{i, j} != {j0, i0}, Sow[{i, j}]];];
If[TriangleArea[p[[j]], p[[Mod[i + 1, n, 1]]],
p[[Mod[j + 1, n, 1]]]] ==
TriangleArea[p[[i]], p[[Mod[i + 1, n, 1]]], p[[Mod[j, n, 1]]]],
If[{i, j} != {j0, i0}, Sow[{i, Mod[j + 1, n, 1]}],
Sow[{Mod[i + 1, n, 1], j}]];];]][[2, 1]]]
pts = RandomReal[{-100, 100}, {50, 2}];
polygonPoints = sortPointsCounterClockwise@convexHull@pts;
antiPodalPairs = GetAllAntiPodalPairs@polygonPoints;
numberOfPolygonPoints = Length[polygonPoints];
calculateDistance =(((dist @@ {polygonPoints[[Mod[#1, numberOfPolygonPoints, 1]]], polygonPoints[[Mod[#2, numberOfPolygonPoints, 1]]]}) &) @@ # &);
calculateDistance /@ antiPodalPairs // Max
(* bruteForce to verify the result. *)
dist @@ # & /@ Subsets[pts, {2}] // Max
GraphicsGrid[
Partition[
Table[Graphics[{(*Draw the polygon*){EdgeForm[Directive[Black]],
FaceForm[None], Polygon[polygonPoints]},(*Draw the anti-
podal pairs*){Red, PointSize[Large],
Point[{polygonPoints[[antiPodalPairs[[i, 1]]]],
polygonPoints[[antiPodalPairs[[i,
2]]]]}]},(*Draw lines connecting anti-
podal pairs*){Blue, Dashed,
Line[{polygonPoints[[antiPodalPairs[[i, 1]]]],
polygonPoints[[antiPodalPairs[[i, 2]]]]}]}}], {i,
Length[antiPodalPairs]}], UpTo[2] (*Maximum 2 plots in a row*)],
Frame -> All]
maxPointsPairs =
MaximalBy[antiPodalPairs, calculateDistance ];
maxPoints =
Part[{polygonPoints[[#1]], polygonPoints[[#2]]} & @@@
maxPointsPairs, 1];
(*Visualization*)ListPlot[pts,
Epilog -> {Red, PointSize[Large], Point[maxPoints], Green,
Line[maxPoints]}, AspectRatio -> 1]
closestPointsDistance✅
Divide-and-conquer Algorithm, $\text{average }\mathcal{O}(n \log n)$
euclideanDistance[p1_, p2_] := EuclideanDistance[p1, p2]
bruteForce[points_] :=
Module[{minDistance = Infinity, dist, minPoints},
Do[dist = euclideanDistance[points[[i]], points[[j]]];
If[dist < minDistance, minDistance = dist;
minPoints = {points[[i]], points[[j]]}], {i,
Length[points] - 1}, {j, i + 1, Length[points]}];
{minDistance, minPoints}]
stripClosest[strip_, d_, pair_] :=
Module[{minDistance = d, dist, minPoints = pair},
Do[dist = euclideanDistance[strip[[i]], strip[[j]]];
If[dist < minDistance, minDistance = dist;
minPoints = {strip[[i]], strip[[j]]}], {i, Length[strip] - 1}, {j,
i + 1, Min[i + 7, Length[strip]]}];
{minDistance, minPoints}]
closestPointsDistance[points_] :=
Module[{mid, midPoint, dl, dr, d, pair, strip, result},
If[Length[points] <= 3, Return[bruteForce[points]]];
mid = Ceiling[Length[points]/2];
midPoint = points[[mid]];
dl = closestPointsDistance[Take[points, mid]];
dr = closestPointsDistance[Drop[points, mid]];
{d, pair} = If[First[dl] < First[dr], dl, dr];
strip = Select[points, Abs[#[[1]] - midPoint[[1]]] < d &];
result = stripClosest[strip, d, pair];
result]
minDistPoints[points_List] :=
Module[{sortedPoints}, sortedPoints = SortBy[points, First];
closestPointsDistance[sortedPoints]]
pts = RandomReal[{-100, 100}, {50, 2}];
{minDist, minPoints} = minDistPoints[pts];
Print[minDist];
(*Visualization*)
ListPlot[pts,
Epilog -> {Red, PointSize[Large], Point[minPoints], Green,
Line[minPoints]}, AspectRatio -> 1]
(* bruteForce to verify the result. *)
euclideanDistance @@ # & /@ Subsets[pts, {2}] // Min
RandomReal[{-100, 100}, {1000000, 2}] // Point // RegionFarthestDistance[#, #] &
$\endgroup$