📜  最小围圈套装1

📅  最后修改于: 2021-04-26 10:38:30             🧑  作者: Mango

先决条件:给定圆上的三个点时的圆方程,凸包

给定一个数组arr [] [] ,该数组包含一个具有整数坐标的二维平面中的N个点。任务是找到最小封闭圆(MEC)的中心和半径。最小封闭圆是所有点都位于圆内或其边界上的圆。

例子:

幼稚的方法:可以通过一些观察来解决此问题。

  • 可以进行的第一个观察是,MEC与至少一个点相交。这是因为,如果MEC在任何点都不相交,则圆可能会进一步缩小,直到它在其中一个点相交为止。
  • 可以得出的第二个观察结果是,给定一个圆包围所有点并在单个点处相交,则可以通过将中心移向该点,同时将点保持在圆边界上,直到圆与一个点相交,从而进一步缩小圆或更多额外积分。
  • 如果圆在两个点(A和B)处相交并且距离AB等于圆直径,则该圆将不再收缩。否则,圆的中心可以朝AB的中点移动,直到圆与第三个点相交(圆不再收缩)。

从以上观察结果可以得出结论,MEC可以:

  1. 与2点A和B相交,其中AB =圆直径。对于这种情况,圆的中心将是AB的中点,半径将是距离AB的一半
  2. 相交3个或更多点。本文已讨论了找到中心和半径的方法。

因此,对于N <= 3,此问题的解决方案是微不足道的。对于其他情况,可以形成一个简单的想法来解决此问题。这个想法是使用所有的点对和三点对来获得定义这些点的圆。获得圆后,测试以查看其他点是否被该圆包围,并返回找到的最小有效圆。

下面是上述方法的实现:

CPP
// C++ program to find the minimum enclosing
// circle for N integer points in a 2-D plane
#include 
#include 
#include 
using namespace std;
  
// Defining infinity
const double INF = 1e18;
  
// Structure to represent a 2D point
struct Point {
    double X, Y;
};
  
// Structure to represent a 2D circle
struct Circle {
    Point C;
    double R;
};
  
// Function to return the euclidean distance
// between two points
double dist(const Point& a, const Point& b)
{
    return sqrt(pow(a.X - b.X, 2) + pow(a.Y - b.Y, 2));
}
  
// Function to check whether a point lies inside
// or on the boundaries of the circle
bool is_inside(const Circle& c, const Point& p)
{
    return dist(c.C, p) <= c.R;
}
  
  
// The following two functions are the functions used
// To find the equation of the circle when three
// points are given.
  
// Helper method to get a circle defined by 3 points
Point get_circle_center(double bx, double by,
                        double cx, double cy)
{
    double B = bx * bx + by * by;
    double C = cx * cx + cy * cy;
    double D = bx * cy - by * cx;
    return { (cy * B - by * C) / (2 * D),
             (bx * C - cx * B) / (2 * D) };
}
  
// Function to return a unique circle that intersects
// three points
Circle circle_from(const Point& A, const Point& B,
                   const Point& C)
{
    Point I = get_circle_center(B.X - A.X, B.Y - A.Y,
                                C.X - A.X, C.Y - A.Y);
    I.X += A.X;
    I.Y += A.Y;
    return { I, dist(I, A) };
}
  
// Function to return the smallest circle
// that intersects 2 points
Circle circle_from(const Point& A, const Point& B)
{
    // Set the center to be the midpoint of A and B
    Point C = { (A.X + B.X) / 2.0, (A.Y + B.Y) / 2.0 };
  
    // Set the radius to be half the distance AB
    return { C, dist(A, B) / 2.0 };
}
  
// Function to check whether a circle encloses the given points
bool is_valid_circle(const Circle& c, const vector& P)
{
  
    // Iterating through all the points to check
    // whether the points lie inside the circle or not
    for (const Point& p : P)
        if (!is_inside(c, p))
            return false;
    return true;
}
  
// Function to return find the minimum enclosing
// circle from the given set of points
Circle minimum_enclosing_circle(const vector& P)
{
  
    // To find the number of points
    int n = (int)P.size();
  
    if (n == 0)
        return { { 0, 0 }, 0 };
    if (n == 1)
        return { P[0], 0 };
  
    // Set initial MEC to have infinity radius
    Circle mec = { { 0, 0 }, INF };
  
    // Go over all pair of points
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
  
            // Get the smallest circle that
            // intersects P[i] and P[j]
            Circle tmp = circle_from(P[i], P[j]);
  
            // Update MEC if tmp encloses all points
            // and has a smaller radius
            if (tmp.R < mec.R && is_valid_circle(tmp, P))
                mec = tmp;
        }
    }
  
    // Go over all triples of points
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            for (int k = j + 1; k < n; k++) {
  
                // Get the circle that intersects P[i], P[j], P[k]
                Circle tmp = circle_from(P[i], P[j], P[k]);
  
                // Update MEC if tmp encloses all points
                // and has smaller radius
                if (tmp.R < mec.R && is_valid_circle(tmp, P))
                    mec = tmp;
            }
        }
    }
    return mec;
}
  
// Driver code
int main()
{
  
    Circle mec = minimum_enclosing_circle({ { 0, 0 },
                                            { 0, 1 },
                                            { 1, 0 } });
  
    cout << "Center = { " << mec.C.X << ", " << mec.C.Y
         << " } Radius = " << mec.R << endl;
  
    Circle mec2 = minimum_enclosing_circle({ { 5, -2 },
                                             { -3, -2 },
                                             { -2, 5 },
                                             { 1, 6 },
                                             { 0, 2 } });
  
    cout << "Center = { " << mec2.C.X << ", " << mec2.C.Y
         << " } Radius = " << mec2.R << endl;
  
    return 0;
}


Python3
# Python3 program to find the minimum enclosing
# circle for N integer points in a 2-D plane
from math import sqrt
  
# Defining infinity
INF = 10**18
  
# Function to return the euclidean distance
# between two points
def dist(a, b):
    return sqrt(pow(a[0] - b[0], 2) + pow(a[1] - b[1], 2))
  
# Function to check whether a point lies inside
# or on the boundaries of the circle
def is_inside(c, p):
    return dist(c[0], p) <= c[1]
  
# The following two functions are the functions used
# To find the equation of the circle when three
# points are given.
  
# Helper method to get a circle defined by 3 points
def get_circle_center(bx, by, cx, cy):
    B = bx * bx + by * by
    C = cx * cx + cy * cy
    D = bx * cy - by * cx
    return [(cy * B - by * C) // (2 * D),
            (bx * C - cx * B) // (2 * D) ]
  
# Function to return a unique circle that intersects
# three points
def circle_frOm(A, B,C):
    I = get_circle_center(B[0] - A[0], B[1] - A[1],
                                C[0] - A[0], C[1] - A[1])
    I[0] += A[0]
    I[1] += A[1]
    return [I, dist(I, A)]
  
# Function to return the smallest circle
# that intersects 2 points
def circle_from(A, B):
      
    # Set the center to be the midpoint of A and B
    C = [ (A[0] + B[0]) / 2.0, (A[1] + B[1]) / 2.0]
  
    # Set the radius to be half the distance AB
    return [C, dist(A, B) / 2.0]
  
# Function to check whether a circle encloses the given points
def is_valid_circle(c, P):
  
    # Iterating through all the points to check
    # whether the points lie inside the circle or not
    for p in P:
        if (is_inside(c, p) == False):
            return False
    return True
  
# Function to return find the minimum enclosing
# circle from the given set of points
def minimum_enclosing_circle(P):
  
    # To find the number of points
    n = len(P)
  
    if (n == 0):
        return [[0, 0], 0]
    if (n == 1):
        return [P[0], 0]
  
    # Set initial MEC to have infinity radius
    mec = [[0, 0], INF]
  
    # Go over all pair of points
    for i in range(n):
        for j in range(i + 1, n):
  
            # Get the smallest circle that
            # intersects P[i] and P[j]
            tmp = circle_from(P[i], P[j])
  
            # Update MEC if tmp encloses all points
            # and has a smaller radius
            if (tmp[1] < mec[1] and is_valid_circle(tmp, P)):
                mec = tmp
  
    # Go over all triples of points
    for i in range(n):
        for j in range(i + 1, n):
            for k in range(j + 1, n):
  
                # Get the circle that intersects P[i], P[j], P[k]
                tmp = circle_frOm(P[i], P[j], P[k])
  
                # Update MEC if tmp encloses all points
                # and has smaller radius
                if (tmp[1] < mec[1] and is_valid_circle(tmp, P)):
                    mec = tmp
  
    return mec
  
# Driver code
  
mec = minimum_enclosing_circle([ [ 0, 0 ],
                                [ 0, 1 ],
                                [ 1, 0 ] ])
  
print("Center = { ",mec[0][1],",",mec[0][1],
                "} Radius = ",round(mec[1],6))
  
mec2 = minimum_enclosing_circle([ [ 5, -2 ],
                                [ -3, -2 ],
                                [ -2, 5 ],
                                [ 1, 6 ],
                                [ 0, 2 ] ])
  
print("Center = {",mec2[0][0],",",mec2[0][1],
        "} Radius = ",mec2[1])
          
# This code is contributed by mohit kumar 29


输出:
Center = { 0.5, 0.5 } Radius = 0.707107
Center = { 1, 1 } Radius = 5

时间复杂度:该解决方案的时间复杂度为O(N 4 ) 。那是因为有N 3个三分点。对于每个三元组,我们检查所有点是否都被圆包围。

方法2:使用凸包概念的解决方案也可以用于此问题。想法是首先在给定的点集上形成凸包。一旦执行了凸包并且返回了新的点集,则可以在新的点集上使用上述解决方案来找到MEC。

此方法的代码与上面的代码相同,除了我们还需要首先获取凸包。请参考本文以获得有效的算法来获得凸包。

时间复杂度:需要观察到的一个问题是,如果输入已经表示凸多边形的某些顶点,则此解决方案将具有与上述简单方法相同的时间复杂度。
因此,这种方法的最坏情况下的复杂度仍然是O(N 4 )

但是,如果凸包的顶点数量远小于N,则复杂度将为O(H 4 + NLog(N)) ,其中H表示凸包的顶点数量,而NLog(N)假设使用Graham Scan算法,因素是找到凸包。

最后,如果凸包的顶点数量H非常小,则可以将其视为一个常数因子,因此时间复杂度将为O(NLog(N))