在距离飞机上其他N点最小距离的位置找到一个点

时间:2014-06-12 15:41:24

标签: c algorithm geometry cartesian-coordinates

我有一行说L行,lx + my + n = 0。现在,假设有一些K点在{L}线上的xy平面上说(x1,y1),(x2, y2)...(xk,yk),我想找到L行上的一个点,比如X (x0,y0),这样X与X之间的距离之和'N'点很少。解决这个问题的算法是什么? 我想到了一个解决方案,首先我发现点的坐标与每个点(x1,y1),(x2, y2)...(xk,yk)的直线L相交。然后我找到所有那些点的平均点,其中垂线遇到线L以找到最小点。但这种做法是错误的。请建议正确的方法来解决问题。这是我的代码

#include <stdio.h>
#include <math.h>
typedef struct
{
     int x;
     int y;
}point;

double distance(point * A, double x, double y)
{
    return sqrt(pow(A->x - x, 2) + pow(A->y - y, 2));
}


int main()
{
    int i = 0 , j = 0, test = 0, number_of_warehouse = 0, A = 0, B = 0, C = 0;
    point * point_array , *closest_points, sum;
    double avgx = 0.0, avgy  = 0.0, Total_dist = 0.0;
    scanf("%d", &test);

    while (test--)
    {
        scanf("%d", &number_of_warehouse);
        point_array = malloc (sizeof(point) * number_of_warehouse);
        closest_points = malloc (sizeof(point) * number_of_warehouse);
        scanf("%d%d%d", &A, &B,&C);
        sum.x = 0;
        sum.y = 0;
        avgx = 0;
        avgy = 0;
        for(i = 0; i < number_of_warehouse; ++i)
        {
            scanf("%d%d", &(point_array[i].x), &(point_array[i].y));
            closest_points[i].x = (B*(B * point_array[i].x - A * point_array[i].y) - A *C)/ (A*A + B*B);
            closest_points[i].y = (A*((-1)* B * point_array[i].x + A * point_array[i].y) - B *C)/ (A*A + B*B);
            sum.x +=  closest_points[i].x;
            sum.y +=  closest_points[i].y;
        }
        Total_dist = 0.0;
        avgx = sum.x / number_of_warehouse;
        avgy = sum.y / number_of_warehouse;
        for(i = 0; i < number_of_warehouse; ++i)
        {
            Total_dist += distance(point_array + i, avgx, avgy);
        }
        printf("%.6f", Total_dist);
    }
    return 0;
}

4 个答案:

答案 0 :(得分:2)

您可以使用least squares的方法进行适当的调整,因为您的问题有点不同。但是,这是一个过程的概述。

如果我们制作a = -l/mb = -n/m,我们可以写:

y = ax + b

这意味着直线上的点的格式为(x ,ax + b),因此直线(x ,y )中的任意点与集合的第i个点之间的距离为(xi, yi)是:

di = sqrt [ (x - xi)^2 + (ax + b - yi)^2 ]

现在我们必须将0的总和最小化为k di,这不是一个小问题。回归分析中通常的近似是最小化距离的平方,这有更简单的解决方案。

扩展我们的正方形和重构:

di^2 = x^2 *k*(1 + a^2) + x (-2xi + 2kab - 2ayi) + (k*b^2 + yi^2 - 2byi + xi^2)

现在,计算di^2i的所有值的总和,并考虑到求和是一个线性算子,我们可以简单地将xi替换为所有值的总和。 xiyixi^2yi^2也是如此(首先要小心计算平方值,然后将它们相加,尽管您不需要这些值)。对于不依赖于i的条款,它们将乘以k,因为它们将加k次。

现在我们推导出di^2的总和,并将其等于零,以求解x

的等式

d (di^2)/dx = 2k(1 + a^2) x + (-2*Sum(xi) + 2ab - 2a*Sum(yi)) = 0

最后,隔离x我们获得:

x = (2*Sum(xi) - 2kab + 2a*Sum(yi))/2k(1+a^2)

y当然是ax + b

因此,您只需要以编程方式计算xiyi的摘要,这应该非常简单。请检查代数,这也是理解该过程的一个很好的练习。

这是一个实现此算法并使用简单案例进行检查的示例程序。如果你的k点集合与你的点可以移动的直线垂直,那么最近的点应该是两条直线的交点。

#include <stdio.h>

#define NUM_OF_POINTS 5

typedef struct
{
 double x;
 double y;
}point;


point findPoint (point *points, double slope, double intercept)
{
int i;
double sum_x = 0, sum_y = 0;
point your_point;


for( i = 0; i < NUM_OF_POINTS; ++i)
{
    sum_x += points[i].x;
    sum_y += points[i].y;
}

your_point.x = 2*(sum_x - NUM_OF_POINTS*slope*intercept + slope*sum_y)/(2*NUM_OF_POINTS*(1+slope*slope));

your_point.y = slope*your_point.x + intercept;

return your_point;


}

int main()
{
    int i = 0;
double slope = 1.0, intercept = 1.0;

    point point_arr [NUM_OF_POINTS], closest_point;

//Generate points in the line normal to y = slope*x + intercept

for (i = 0; i < NUM_OF_POINTS; i++)
{
    point_arr[i].x = 2.0 + i;
    point_arr[i].y = (-1.0/slope)*point_arr[i].x + 3.0;
}

closest_point = findPoint (point_arr, slope, intercept);
printf("Your point is %f, %f\n", closest_point.x, closest_point.y);


return 0;
}

直线为y = x + 1,点在法线y = -x + 3中生成。结果是点(1,2),它是两条线的交点。

DP

答案 1 :(得分:1)

点P的轨迹使得P与给定的固定n点(不是所有共线)的距离之和是恒定的(比如说D)是一个凸集,称为n-椭圆,相应的n-椭圆用于距离对于D',对于D',D'完全位于n-椭圆内。 D.(2椭圆是实际的椭圆)

因此,在给定线上的点之间存在唯一的(全局)最小值,并且爬山算法将起作用。

请参阅此文件:n-ellipses and the minimum distance problem以获取上述声明的证据。

@DissidentPenguin:最小化x_1 + x_2与最小化sqrt(x_1)+ sqrt(x_2)(你对sqrt(x_1 + x_2)混淆)不同。

答案 2 :(得分:1)

您可以在数学上将解决方案表示为: -

lx + my + n = 0

and 

Say you want to minimize squared distances then :-

S = sum((xk-x)^2 + (yk-y)^2) for all N points.

y = (n-mx)/l from line equation

S = sum((xk-x)^2 + (yk-(n-mx)/l)^2)

S = sum((xk-x)^2 + (ykl-n+mx)^2/l^2))

Diff wrt to x for minimizing 

S = sum(2*(xk-x) + 2m*(ykl-n+mx)/l^2) = 0

Distributing summation

S = sum(xk) - sum(x) + (m/l^2)*( l*sum(yk) - sum(n) + m*sum(x))) = 0

S = sum(xk) - N*x + (m/l^2)*(l*sum(yk) - N*n + m*N*x) = 0

(1-m^2/l^2)*N*x = sum(xk) + (m/l^2)*(l*sum(yk)-N*n)

Find x using the equation and then find y using line equation 

答案 3 :(得分:-1)

我认为没有封闭形式的解决方案。

这意味着您必须使用数字近似值。

通用

即,您需要实现函数double tot_dist(double x),其中x是您线上的坐标,tot_dist计算距离的总和。

然后,您需要将其传递给可用的大量最小发现功能之​​一(例如,Mathematica,R:optimoptimize)。

特定问题

如果您将点x沿着线移动一小段距离hxX_k之间的距离将会更改h*cos(a)其中{ {1}}是第a行与第L行之间的角度。这意味着您的解决方案满足了很好的等式xX_k,即,您可以使用Newton's method来查找sum(cos(a_k))=0函数的零(而不是使用通用最小化器)(您将需要计算double sum_cos_angle(double x)的导数,但这不应该是一个大问题。

沃罗诺伊

建议的Voronoi diagram解决方案 不适用于此处,因为这是全局问题(距所有点的距离),以及Voronoi用于本地问题(最接近的一个点)。基本上,如果我们摆动一个点sum_cos_angle,这个问题的解决方案就会改变。另一方面,Voronoi图仅取决于最接近的2个点的位置,因此,如果我们摆动单个点,图表将不会改变,因此,使用它获得的任何解决方案都将不要改变。