给定平面上的N个点(形式为(x,y)),找到在其上具有最大点数的圆? 附言:该点应位于圆的圆周上。 解决此问题的最有效算法是什么?它如何工作?您将使用哪种数据结构来解决此问题。这是在FANG编码采访之一中问到的。
答案 0 :(得分:4)
作为起点,简单的 O(N 3 )解决方案是找到与每个唯一三重点相对应的圆,同时计算出现的次数找到的每个圆圈。
如果一个圆上有 N 个点,那么您会发现它 N-choose-3 次,因此,您最常找到的圆是带有大部分要点。
在任何实际的实现中都存在复杂性,但是它们是不同的复杂性,具体取决于您的观点的表示方式以及您要的是精确的还是近似的答案。
答案 1 :(得分:2)
案例1:霍夫变换
在计算机视觉问题解决中,我们经常在边缘信息中搜索圆。此问题的特征在于,在存在大量噪声的情况下,有许多数据点可能源自许多不同的圈子。
解决此问题的常用方法是霍夫变换 https://en.wikipedia.org/wiki/Circle_Hough_Transform。基本思想是总结可以通过每个点(x,y)的圆的证据。
我们创建一个称为Hough [a,b,r]的整数数组,该数组对可以通过您的点(x,y)的所有可能的圆进行参数化。这等效于在以(x,y)为中心的r = 1平面上绘制半径为1的圆;在r = 2平面上以(x,y)等为中心的半径2的圆。
每次通过[a,b,r]中的一个点绘制一个圆时,我们都会在相应的值上加上1。有些观点积累了很多证据。这些点对应于感兴趣的圈子。
cis.rit.edu的图像说明了在r平面之一中发生的情况。
对每个点(x,y)进行此操作将针对[a,b,r]中与您要寻找的圆对应的每个点生成证据。因此,只需扫描此阵列即可找到具有最大证据的点。那是你的圈子。
霍夫变换的例子
知道圆的半径会将其从O(n ^ 3)问题减少到O(n ^ 2)问题,因为只需要构造和扫描一个平面。在对数空间中绘制半径以给出(精度不高)O(n ^ 2 log n)算法时,我也取得了不错的结果。
案例2:圆形拟合
如果已知这些点位于单个圆的边界附近,并且/或者如果这些点不是很多,和/或我们非常确定噪声很小,那么霍夫变换是一个不好的解决方案由于它需要大量的计算,因此会占用大量内存,并且由于累加器阵列的光栅性质,它可能不太准确。
在这种情况下,我们可能想要类似于使用线性回归的线拟合技术来拟合圆。在https://pdfs.semanticscholar.org/faac/44067f04abf10af7dd583fca0c35c5937f95.pdf中可以找到关于圆拟合技术的讨论。
下面介绍了该算法的一种(相当简单的实现)。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
typedef struct {
float x;
float y;
} point;
/*
* Function using a modified least squares approach to circle fitting.
*
* Reference :
*
* Umbach, D. and Jones, K. N. "A few methods for fitting circles to data",
* IEEE Trans Instrumentation and Measurement
* vol XX(Y) 2000
*
* https://pdfs.semanticscholar.org/faac/44067f04abf10af7dd583fca0c35c5937f95.pdf
*
* NOTES
*
* The code below has not been checked for numerical stability or conditioning.
*/
int circle_fit_MLS (point P[], int n, double *x_pos, double *y_pos, double *radius)
{
int i;
double sum_x=0.0, sum_y=0.0, sum_xx=0.0, sum_xy=0.0, sum_yy=0.0, sum_xyy=0.0, sum_yxx=0.0, sum_xxx=0.0, sum_yyy=0.0;
double A, B, C, D, E;
double x2, y2, xy, F, xdif, ydif;
for (i=0; i<n; i++)
{
sum_x += P[i].x;
sum_y += P[i].y;
x2 = P[i].x * P[i].x;
y2 = P[i].y * P[i].y;
xy = P[i].x * P[i].y;
sum_xx += x2;
sum_yy += y2;
sum_xy += xy;
sum_xyy += xy*P[i].y;
sum_yxx += P[i].y*x2;
sum_xxx += x2*P[i].x;
sum_yyy += y2*P[i].y;
}
A = n * sum_xx - sum_x * sum_x;
B = n * sum_xy - sum_x * sum_y;
C = n * sum_yy - sum_y * sum_y;
D = 0.5 * ( n * (sum_xyy + sum_xxx) - sum_x * sum_yy - sum_x * sum_xx);
E = 0.5 * ( n * (sum_yxx + sum_yyy) - sum_y * sum_xx - sum_y * sum_yy);
F = A*C - B*B;
*x_pos = (D*C - B*E) / F;
*y_pos = (A*E - B*D) / F;
*radius = 0;
for (i=0; i<n; i++)
{
xdif = P[i].x - *x_pos;
ydif = P[i].y - *y_pos;
*radius += sqrt(xdif * xdif + ydif * ydif);
}
*radius /= n;
return 0;
}
下面的主程序可用于测试代码。请发回任何结果/观察结果/建议以改善评论。
int main()
{
point *P;
int n, i;
double xpos, ypos, radius;
printf ("Please enter the number of points \n> ");
scanf ("%d", &n);
P = malloc (n * sizeof(point));
for (i=0; i<n; i++)
{
printf ("x%d = ", i);
scanf ("%f", &P[i].x);
printf ("y%d = ", i);
scanf ("%f", &P[i].y);
}
circle_fit_MLS (P, n, &xpos, &ypos, &radius);
printf (" a = %f\n", xpos);
printf (" b = %f\n", ypos);
printf (" r = %f\n", radius);
}