📌  相关文章
📜  使用旋转卡尺方法在坐标平面中两点之间的最大距离

📅  最后修改于: 2021-04-29 15:29:51             🧑  作者: Mango

先决条件: Graham Scan的凸包,方向。
给定坐标平面中的N个点集,任务是找到给定平面集中任意两个点之间的最大距离。

例子:

幼稚的方法:幼稚的想法是尝试从给定的集合中每对可能的点进行计算,计算它们之间的距离,并打印所有对之间的最大距离。

下面是上述方法的实现:

C++
#include 
using namespace std;
 
// Function calculates distance
// between two points
long dist(pair p1,
          pair p2)
{
    long x0 = p1.first - p2.first;
    long y0 = p1.second - p2.second;
    return x0 * x0 + y0 * y0;
}
 
// Function to find the maximum
// distance between any two points
double maxDist(pair p[], int n)
{
    double Max = 0;
 
    // Iterate over all possible pairs
    for(int i = 0; i < n; i++)
    {
        for(int j = i + 1; j < n; j++)
        {
             
            // Update max
            Max = max(Max, (double)dist(p[i],
                                        p[j]));
        }
    }
 
    // Return actual distance
    return sqrt(Max);
}
 
// Driver code  
int main()
{
     
    // Number of points
    int n = 5;
 
    pair p[n];
 
    // Given points
    p[0].first = 4, p[0].second = 0;
    p[1].first = 0, p[1].second = 2;
    p[2].first = -1, p[2].second = -7;
    p[3].first = 1, p[3].second = 10;
    p[4].first = 2, p[4].second = -3;
 
    // Function call
    cout << fixed << setprecision(14)
         << maxDist(p, n) <


Java
import java.awt.*;
import java.util.ArrayList;
 
public class Main {
 
    // Function calculates distance
    // between two points
    static long dist(Point p1, Point p2)
    {
        long x0 = p1.x - p2.x;
        long y0 = p1.y - p2.y;
        return x0 * x0 + y0 * y0;
    }
 
    // Function to find the maximum
    // distance between any two points
    static double maxDist(Point p[])
    {
        int n = p.length;
        double max = 0;
 
        // Iterate over all possible pairs
        for (int i = 0; i < n; i++) {
 
            for (int j = i + 1; j < n; j++) {
 
                // Update max
                max = Math.max(max,
                               dist(p[i],
                                    p[j]));
            }
        }
 
        // Return actual distance
        return Math.sqrt(max);
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        // Number of points
        int n = 5;
 
        Point p[] = new Point[n];
 
        // Given points
        p[0] = new Point(4, 0);
        p[1] = new Point(0, 2);
        p[2] = new Point(-1, -7);
        p[3] = new Point(1, 10);
        p[4] = new Point(2, -3);
 
        // Function Call
        System.out.println(maxDist(p));
    }
}


Python3
from math import sqrt
 
# Function calculates distance
# between two points
def dist(p1, p2):
     
    x0 = p1[0] - p2[0]
    y0 = p1[1] - p2[1]
    return x0 * x0 + y0 * y0
 
# Function to find the maximum
# distance between any two points
def maxDist(p):
 
    n = len(p)
    maxm = 0
 
    # Iterate over all possible pairs
    for i in range(n):
        for j in range(i + 1, n):
             
            # Update maxm
            maxm = max(maxm, dist(p[i], p[j]))
 
    # Return actual distance
    return sqrt(maxm)
     
# Driver Code
if __name__ == '__main__':
     
    # Number of points
    n = 5
 
    p = []
     
    # Given points
    p.append([4, 0])
    p.append([0, 2])
    p.append([-1, -7])
    p.append([1, 10])
    p.append([2, -3])
 
    # Function Call
    print(maxDist(p))
 
# This code is contributed by mohit kumar 29


C#
using System;
class GFG {
     
    // Function calculates distance
    // between two points
    static long dist(Tuple p1, Tuple p2)
    {
        long x0 = p1.Item1 - p2.Item1;
        long y0 = p1.Item2 - p2.Item2;
        return x0 * x0 + y0 * y0;
    }
  
    // Function to find the maximum
    // distance between any two points
    static double maxDist(Tuple[] p)
    {
        int n = p.Length;
        double max = 0;
  
        // Iterate over all possible pairs
        for (int i = 0; i < n; i++) {
  
            for (int j = i + 1; j < n; j++) {
  
                // Update max
                max = Math.Max(max, dist(p[i],p[j]));
            }
        }
  
        // Return actual distance
        return Math.Sqrt(max);
    }
 
  // Driver code
  static void Main() {
      
        // Given points
        Tuple[] p =
        {
            Tuple.Create(4, 0),
            Tuple.Create(0, 2),
            Tuple.Create(-1, -7),
            Tuple.Create(1, 10),
            Tuple.Create(2, -3),
        };
       
        // Function Call
        Console.WriteLine(maxDist(p));
  }
}
 
// This code is contributed by divyesh072019


Java
// Java Program for the above approach
import java.awt.*;
import java.util.*;
import java.util.Map.Entry;
 
public class Main {
 
    // Function to detect the orientation
    static int orientation(Point p,
                           Point q,
                           Point r)
    {
        int x = area(p, q, r);
 
        // If area > 0 then
        // points are clockwise
        if (x > 0) {
            return 1;
        }
 
        // If area<0 then
        // points are counterclockwise
        if (x < 0) {
            return -1;
        }
 
        // If area is 0 then p, q
        // and r are co-linear
        return 0;
    }
 
    // Function to find the area
    static int area(Point p, Point q, Point r)
    {
        // 2*(area of triangle)
        return ((p.y - q.y) * (q.x - r.x)
                - (q.y - r.y) * (p.x - q.x));
    }
 
    // Function to find the absolute Area
    static int absArea(Point p,
                       Point q, Point r)
    {
        // Unsigned area
        // 2*(area of triangle)
        return Math.abs(area(p, q, r));
    }
 
    // Function to find the distance
    static int dist(Point p1, Point p2)
    {
        // squared-distance b/w
        // p1 and p2 for precision
        return ((p1.x - p2.x) * (p1.x - p2.x)
                + (p1.y - p2.y) * (p1.y - p2.y));
    }
 
    // Function to implement Convex Hull
    // Approach
    static ArrayList
    convexHull(Point points[])
    {
        int n = points.length;
 
        Point min = new Point(Integer.MAX_VALUE,
                              Integer.MAX_VALUE);
 
        // Choose point having min.
        // y-coordinate and if two points
        // have same y-coordinate choose
        // the one with minimum x-coordinate
        int ind = -1;
 
        // Iterate Points[]
        for (int i = 0; i < n; i++) {
            if (min.y > points[i].y) {
                min.y = points[i].y;
                min.x = points[i].x;
                ind = i;
            }
            else if (min.y == points[i].y
                     && min.x > points[i].x) {
                min.x = points[i].x;
                ind = i;
            }
        }
        points[ind] = points[0];
        points[0] = min;
 
        // Sort points which have
        // minimum polar angle wrt
        // Point min
        Arrays.sort(points, 1, n,
                    new Comparator() {
 
                        @Override
                        public int compare(Point o1,
                                           Point o2)
                        {
 
                            int o = orientation(min, o1, o2);
 
                            // If points are co-linear
                            // choose the one having smaller
                            // distance with min first.
                            if (o == 0) {
                                return dist(o1, min)
                                    - dist(o2, min);
                            }
 
                            // If clockwise then swap
                            if (o == 1) {
                                return 1;
                            }
 
                            // If anticlockwise then
                            // don't swap
                            return -1;
                        }
                    });
 
        Stack st = new Stack<>();
 
        // First hull point
        st.push(points[0]);
 
        int k;
        for (k = 1; k < n - 1; k++) {
            if (orientation(points[0],
                            points[k],
                            points[k + 1])
                != 0)
                break;
        }
 
        // Second hull point
        st.push(points[k]);
 
        for (int i = k + 1; i < n; i++) {
            Point top = st.pop();
 
            while (orientation(st.peek(),
                               top,
                               points[i])
                   >= 0) {
                top = st.pop();
            }
 
            st.push(top);
            st.push(points[i]);
        }
 
        ArrayList hull
            = new ArrayList<>();
 
        // Iterate stack and add node to hull
        for (int i = 0; i < st.size(); i++) {
            hull.add(st.get(i));
        }
        return hull;
    }
 
    // Function to find the maximum
    // distance between any two points
    // from a set of given points
    static double
    rotatingCaliper(Point points[])
    {
        // Takes O(n)
        ArrayList convexHull
            = convexHull(points);
        int n = convexHull.size();
 
        // Convex hull point in counter-
        // clockwise order
        Point hull[] = new Point[n];
        n = 0;
 
        while (n < convexHull.size()) {
            hull[n] = convexHull.get(n++);
        }
 
        // Base Cases
        if (n == 1)
            return 0;
        if (n == 2)
            return Math.sqrt(dist(hull[0], hull[1]));
        int k = 1;
 
        // Find the farthest vertex
        // from hull[0] and hull[n-1]
        while (absArea(hull[n - 1],
                       hull[0],
                       hull[(k + 1) % n])
               > absArea(hull[n - 1],
                         hull[0],
                         hull[k])) {
            k++;
        }
 
        double res = 0;
 
        // Check points from 0 to k
        for (int i = 0, j = k;
             i <= k && j < n; i++) {
            res = Math.max(res,
                           Math.sqrt((double)dist(hull[i],
                                                  hull[j])));
 
            while (j < n
                   && absArea(hull[i],
                              hull[(i + 1) % n],
                              hull[(j + 1) % n])
                          > absArea(hull[i],
                                    hull[(i + 1) % n],
                                    hull[j])) {
 
                // Update res
                res = Math.max(
                    res,
                    Math.sqrt(dist(hull[i],
                                   hull[(j + 1) % n])));
                j++;
            }
        }
 
        // Return the result distance
        return res;
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        // Total points
        int n = 5;
        Point p[] = new Point[n];
 
        // Given Points
        p[0] = new Point(4, 0);
        p[1] = new Point(0, 2);
        p[2] = new Point(-1, -7);
        p[3] = new Point(1, 10);
        p[4] = new Point(2, -3);
 
        // Function Call
        System.out.println(rotatingCaliper(p));
    }
}


输出:
17.11724276862369

时间复杂度: O(N 2 ),其中N是总点数。
辅助空间: O(1)

高效的方法:可以使用“旋转卡尺”方法优化上述幼稚的方法

为了实现上述方法,我们将使用凸包的概念。在开始进一步讨论最佳方法之前,我们需要了解以下内容:

  • 三角形的无符号区域:如果给定三个点P1(x1,y1),P2(x2,y2)P3(x3,y3),

\left ( \frac {(x2 - x1)*(y3 - y2) - (x3 - x2)*(y2 - y1)}{2} \right )

  • 是三角形的有符号区域。如果面积为正,则三个点按顺时针顺序排列;否则,它们按逆时针顺序;如果面积等于零,则这些点共线。如果我们取绝对值,那么它将代表三角形的无符号区域。在这里,无符号基本上表示没有方向的区域,即,我们只需要区域的相对绝对值即可。因此,我们可以从公式中删除(1/2)。因此,
  • 对映点:那些在直径上完全相反的点。但是对我们而言,对映点是在凸多边形中彼此相距最远的点。如果我们从给定的集合中选择一个点,则只有当我们能够从给定的集合中找到它的对映点时,该点才能达到其最大距离。

步骤如下:

  1. 具有最大距离的两个点必须位于由给定集合形成的凸多边形的边界上。因此,使用Graham Scan的凸包方法以逆时针顺序排列点。
  2. 我们有N个点,最初从点P1开始,并包括给定点集中的那些点,这样,通过包括该点集中的任何点,区域的面积始终会增加。
  3. 从点P1开始,选择K = 2并在面积(PN,P1,PK)增加的同时增加K 并在开始减少之前停止。现在,当前点PK可能是P1的对映点。类似地,通过找到面积(P1,P2,PK)并递增我们先前停下来的K形式来找到p2的对映点。
  4. 继续更新每个对映点的最大距离是在上述步骤中发生的,因为初始点和点之间的距离(包括区域在内)是最大的。

下面是上述方法的实现:

Java

// Java Program for the above approach
import java.awt.*;
import java.util.*;
import java.util.Map.Entry;
 
public class Main {
 
    // Function to detect the orientation
    static int orientation(Point p,
                           Point q,
                           Point r)
    {
        int x = area(p, q, r);
 
        // If area > 0 then
        // points are clockwise
        if (x > 0) {
            return 1;
        }
 
        // If area<0 then
        // points are counterclockwise
        if (x < 0) {
            return -1;
        }
 
        // If area is 0 then p, q
        // and r are co-linear
        return 0;
    }
 
    // Function to find the area
    static int area(Point p, Point q, Point r)
    {
        // 2*(area of triangle)
        return ((p.y - q.y) * (q.x - r.x)
                - (q.y - r.y) * (p.x - q.x));
    }
 
    // Function to find the absolute Area
    static int absArea(Point p,
                       Point q, Point r)
    {
        // Unsigned area
        // 2*(area of triangle)
        return Math.abs(area(p, q, r));
    }
 
    // Function to find the distance
    static int dist(Point p1, Point p2)
    {
        // squared-distance b/w
        // p1 and p2 for precision
        return ((p1.x - p2.x) * (p1.x - p2.x)
                + (p1.y - p2.y) * (p1.y - p2.y));
    }
 
    // Function to implement Convex Hull
    // Approach
    static ArrayList
    convexHull(Point points[])
    {
        int n = points.length;
 
        Point min = new Point(Integer.MAX_VALUE,
                              Integer.MAX_VALUE);
 
        // Choose point having min.
        // y-coordinate and if two points
        // have same y-coordinate choose
        // the one with minimum x-coordinate
        int ind = -1;
 
        // Iterate Points[]
        for (int i = 0; i < n; i++) {
            if (min.y > points[i].y) {
                min.y = points[i].y;
                min.x = points[i].x;
                ind = i;
            }
            else if (min.y == points[i].y
                     && min.x > points[i].x) {
                min.x = points[i].x;
                ind = i;
            }
        }
        points[ind] = points[0];
        points[0] = min;
 
        // Sort points which have
        // minimum polar angle wrt
        // Point min
        Arrays.sort(points, 1, n,
                    new Comparator() {
 
                        @Override
                        public int compare(Point o1,
                                           Point o2)
                        {
 
                            int o = orientation(min, o1, o2);
 
                            // If points are co-linear
                            // choose the one having smaller
                            // distance with min first.
                            if (o == 0) {
                                return dist(o1, min)
                                    - dist(o2, min);
                            }
 
                            // If clockwise then swap
                            if (o == 1) {
                                return 1;
                            }
 
                            // If anticlockwise then
                            // don't swap
                            return -1;
                        }
                    });
 
        Stack st = new Stack<>();
 
        // First hull point
        st.push(points[0]);
 
        int k;
        for (k = 1; k < n - 1; k++) {
            if (orientation(points[0],
                            points[k],
                            points[k + 1])
                != 0)
                break;
        }
 
        // Second hull point
        st.push(points[k]);
 
        for (int i = k + 1; i < n; i++) {
            Point top = st.pop();
 
            while (orientation(st.peek(),
                               top,
                               points[i])
                   >= 0) {
                top = st.pop();
            }
 
            st.push(top);
            st.push(points[i]);
        }
 
        ArrayList hull
            = new ArrayList<>();
 
        // Iterate stack and add node to hull
        for (int i = 0; i < st.size(); i++) {
            hull.add(st.get(i));
        }
        return hull;
    }
 
    // Function to find the maximum
    // distance between any two points
    // from a set of given points
    static double
    rotatingCaliper(Point points[])
    {
        // Takes O(n)
        ArrayList convexHull
            = convexHull(points);
        int n = convexHull.size();
 
        // Convex hull point in counter-
        // clockwise order
        Point hull[] = new Point[n];
        n = 0;
 
        while (n < convexHull.size()) {
            hull[n] = convexHull.get(n++);
        }
 
        // Base Cases
        if (n == 1)
            return 0;
        if (n == 2)
            return Math.sqrt(dist(hull[0], hull[1]));
        int k = 1;
 
        // Find the farthest vertex
        // from hull[0] and hull[n-1]
        while (absArea(hull[n - 1],
                       hull[0],
                       hull[(k + 1) % n])
               > absArea(hull[n - 1],
                         hull[0],
                         hull[k])) {
            k++;
        }
 
        double res = 0;
 
        // Check points from 0 to k
        for (int i = 0, j = k;
             i <= k && j < n; i++) {
            res = Math.max(res,
                           Math.sqrt((double)dist(hull[i],
                                                  hull[j])));
 
            while (j < n
                   && absArea(hull[i],
                              hull[(i + 1) % n],
                              hull[(j + 1) % n])
                          > absArea(hull[i],
                                    hull[(i + 1) % n],
                                    hull[j])) {
 
                // Update res
                res = Math.max(
                    res,
                    Math.sqrt(dist(hull[i],
                                   hull[(j + 1) % n])));
                j++;
            }
        }
 
        // Return the result distance
        return res;
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        // Total points
        int n = 5;
        Point p[] = new Point[n];
 
        // Given Points
        p[0] = new Point(4, 0);
        p[1] = new Point(0, 2);
        p[2] = new Point(-1, -7);
        p[3] = new Point(1, 10);
        p[4] = new Point(2, -3);
 
        // Function Call
        System.out.println(rotatingCaliper(p));
    }
}
输出:
17.11724276862369

时间复杂度: O(N * log N)
辅助空间: O(N)