先决条件:给定圆上三个点时的圆方程,凸包
给定一个数组arr[][]在具有整数坐标的二维平面中包含N 个点。任务是找到最小包围圆(MEC)的中心和半径。最小封闭圆是所有点都位于圆内或其边界上的圆。
例子:
Input: arr[][] = {{0, 0}, {0, 1}, {1, 0}}
Output: Center = {0.5, 0.5}, Radius = 0.7071
Explanation:
On plotting the above circle with radius 0.707 and center (0.5, 0.5), it can be observed clearly that all the mentioned points lie either inside or on the circle.
Input: arr[][] = {{5, -2}, {-3, -2}, {-2, 5}, {1, 6}, {0, 2}}
Output: Center = {1.0, 1.0}, Radius = 5.000
朴素的方法:这个问题可以通过进行一些观察来解决。
- 可以进行的第一个观察是 MEC 至少与一个点相交。这是因为如果 MEC 在任何一点都不相交,那么圆圈可能会进一步缩小,直到它在其中一个点相交。
- 可以进行的第二个观察是,给定一个包含所有点并相交于一个点的圆,可以通过将中心移向该点同时将点保持在圆边界上直到圆与一个相交来进一步缩小圆或更多的附加点。
- 如果圆相交于两点(A 和 B)并且距离AB等于圆的直径,则圆不能再收缩。否则,圆的中心可以移向 AB 的中点,直到圆与第三个点相交(圆不能再收缩)。
根据上述观察,可以得出结论,MEC 要么:
- 与 2 个点 A 和 B 相交,其中 AB = 圆直径。对于这种情况,圆的中心将是A和B的中点,半径将是距离 AB 的一半。
- 与 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) factor 用于在使用 Graham Scan 算法的情况下找到凸包。
最后,如果凸包的顶点数 H非常小,则可以将其视为常数因子,因此时间复杂度为O(NLog(N)) 。