Minimum Enclosing Circle
Prerequisites: Equation of circle when three points on the circle are given, Convex Hull
Given an array arr[][] containing N points in a 2-D plane with integer coordinates. The task is to find the centre and the radius of the minimum enclosing circle(MEC). A minimum enclosing circle is a circle in which all the points lie either inside the circle or on its boundaries.
Examples:
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
Naive Approach: This problem can be solved by making a few observations.
- The first observation which can be made is that the MEC intersects at least one point. That’s because if the MEC does not intersect at any point, then the circle could be further shrunk until it intersects at one of the points.
- The second observation which can be made is that given a circle that encloses all the points and intersects at a single point, the circle can further be shrunk by moving the centre towards that point while keeping the point on the circle boundary until the circle intersects one or more additional points.
- If the circle intersects at two points(A and B) and the distance AB is equal to the circle diameter, then the circle cannot be shrunk anymore. Else, the centre of the circle can be moved towards the midpoint of AB until the circle intersects a third point(at which the circle cannot be shrunk anymore).
From the above observations, it can be concluded that the MEC either:
- Intersects 2 points A and B, where AB = circle diameter. For this case, the circle’s centre would be the midpoint of A and B and the radius would be half of the distance AB.
- Intersects 3 or more points. The approach to find the center and radius has been discussed in this article.
Thus, the solution to this problem is trivial for N <= 3. For other cases, a simple idea can be formed to solve this problem. The idea is to use all pairs and triples of points to obtain the circle defined those points. After obtaining the circle, test to see if the other points are enclosed by that circle and return the smallest valid circle found.
Below is the implementation of the above approach:
CPP
// C++ program to find the minimum enclosing // circle for N integer points in a 2-D plane #include <iostream> #include <math.h> #include <vector> 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<Point>& 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<Point>& 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 |
Javascript
// JS program to find the minimum enclosing // circle for N integer points in a 2-D plane // Defining infinity let INF = 1e18; // Structure to represent a 2D point class Point { constructor(a = 0, b = 0) { this .X = a; this .Y = a; } }; // Structure to represent a 2D circle class Circle { constructor(a = new Point(0, 0), b = 0) { this .C = a; this .R = b; } }; // Function to return the euclidean distance // between two points function dist(a, b) { return Math.sqrt(Math.pow(a.X - b.X, 2) + Math.pow(a.Y - b.Y, 2)); } // Function to check whether a point lies inside // or on the boundaries of the circle function is_inside(c, 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 function get_circle_center(bx, by, cx, cy) { let B = bx * bx + by * by; let C = cx * cx + cy * cy; let 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 function circle_from(A, B, C) { let 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 function circle_from(A, B) { // Set the center to be the midpoint of A and B let C = new Point((A.X + B.X) / 2.0, (A.Y + B.Y) / 2.0); // Set the radius to be half the distance AB return new Circle(C, dist(A, B) / 2.0); } // Function to check whether a circle encloses the given points function is_valid_circle(c, P) { // Iterating through all the points to check // whether the points lie inside the circle or not for ( var p of P) if (!is_inside(c, p)) return false ; return true ; } // Function to return find the minimum enclosing // circle from the given set of points function minimum_enclosing_circle(P) { // To find the number of points let n = P.length; if (n == 0) return new Circle(); if (n == 1) return [P[0], 0 ]; // Set initial MEC to have infinity radius let mec = new Circle( new Point(0, 0), INF ); // Go over all pair of points for ( var i = 0; i < n; i++) { for ( var j = i + 1; j < n; j++) { // Get the smallest circle that // intersects P[i] and P[j] let 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 ( var i = 0; i < n; i++) { for ( var j = i + 1; j < n; j++) { for ( var k = j + 1; k < n; k++) { // Get the circle that intersects P[i], P[j], P[k] let 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 let mec = minimum_enclosing_circle([ new Point( 0, 0 ), new Point( 0, 1 ), new Point( 1, 0 )]); console.log( "Center = { " + mec.C.X + ", " + mec.C.Y + " } Radius = " + mec.R); let mec2 = minimum_enclosing_circle([ new Point( 5, -2), new Point(-3, -2), new Point(-2, 5), new Point( 1, 6), new Point( 0, 2)]); console.log( "Center = { " + mec2.C.X + ", " + mec2.C.Y + " } Radius = " + mec2.R); // This code is contributed by phasing17 |
Center = { 0.5, 0.5 } Radius = 0.707107 Center = { 1, 1 } Radius = 5
Time Complexity: The time complexity for this solution would be of O(N4). That’s because there are N3 triples of points. And for each triple, we check if all the points are enclosed by the circle.
Approach 2: A solution with the application of convex hull concept can also be used for this problem. The idea is to first form a convex hull on the given set of points. Once the convex hull is performed and the new set of points is returned, then the above-mentioned solution can be used on the new set of points to find the MEC.
The code for this approach would be the same as above except that we would also need to get the convex hull first. Please refer to this article for an efficient algorithm to get the convex hull.
Time Complexity: One observation that needs to be made that if the input already represents some vertices of a convex polygon, then this solution would have the same time complexity of the above naive approach.
Therefore, the worst-case complexity of this approach is still O(N4).
However, if the number of vertices of the convex hull is considerably smaller than N, then the complexity would be O(H4 + NLog(N)) where H represents the number of vertices of the convex hull, and the NLog(N) factor is for finding the convex hull assuming Graham Scan algorithm is used.
Finally, if the number of vertices, H, of the convex hull, is very small, then it can be considered as a constant factor and thus the time complexity would be O(NLog(N)).
Please Login to comment...