将两条线拟合到一组二维点

Fitting two lines to a set of 2D points

给定一组二维点(图中的黑点)我需要选择两条线以某种方式表示这些点。我可能需要最小化 [距离 x 到两条线中较近的距离]^2 的平方和,尽管如果有任何其他指标使这更容易,这也很好。

明显但无效的方法是在所有 2^n 分区上尝试最小二乘法。一种实用的方法可能是迭代改进,也许从随机分区开始。

是否有研究处理这个问题的算法?

我想到了文献中的两个相关概念。

首先,我的直觉是应该有一种方法可以将这个问题解释为估计 mixture model. I haven't worked out the details, since parameter estimation typically uses expectation--maximization 的参数,我可以描述它是如何工作的:将一个分区初始化为两部分,然后交替 运行 对每个部分进行回归,并根据它们与新回归线的距离重新分配点。

其次,假设输入相对干净,您应该能够使用 RANSAC 获得良好的初始分区。对于一些小的 k,取两个不相交的 k 点随机样本,并通过它们拟合线,然后每隔一个点分配一次。为了获得 (100 − x)% 的成功机会,您需要重复 ln(100/x) × 22k−1 次并选择最好的一个。

我认为这可以表述为一个显式优化问题:

 min sum(j, r1(j)^2 + r2(j)^2)                 (quadratic)
 subject to
     r1(j) = (y(j) - a0 - a1*x(j)) * δ(j)      (quadratic)
     r2(j) = (y(j) - b0 - b1*x(j)) * (1-δ(j))  (quadratic) 
     δ(j) ∈ {0,1}

我们同时进行点到线的分配和回归(残差平方和的最小化)。

这是一个非凸二次约束混合整数二次规划问题。可以处理此问题的求解器包括 Gurobi、Baron、Couenne、Antigone。

我们可以重新表述一下:

 min sum(j, r(j)^2)                      (convex quadratic)
 subject to
     r(j) = y(j) - a0 - a1*x(j) + s1(j)  (one of these will be relaxed)
     r(j) = y(j) - b0 - b1*x(j) + s2(j)  (all linear)
     -U*δ(j) <= s1(j) <= U*δ(j)
     -U*(1-δ(j)) <= s2(j) <= U*(1-δ(j))
     δ(j) ∈ {0,1}
     s1(j),s2(j) ∈ [-U,U]
     U = 1000                            (reasonable bound, constant)

这使它成为一个直 凸 MIQP 模型。这将允许使用更多求解器(例如 Cplex)并且更容易求解。其他一些公式是 here。提到的一些模型不需要我在上面的 big-M 公式中必须使用的边界。值得注意的是,这些模型提供了经过验证的最佳解决方案(对于非凸模型,这需要全局求解器;凸模型更容易,不需要这个)。此外,除了最小二乘 objective,我们还可以形成 L1 objective。在后一种情况下,我们最终得到一个线性 MIP 模型。

一个小测试证实了这一点:

这道题有 50 分,在慢速笔记本电脑上使用 Cplex 的 MIQP 求解器需要 1.25 秒。这可能是一个统计问题的案例,其中 MIP/MIQP 方法可以提供一些东西。

在 OPL CPLEX 中,如果我从示例开始 curve fitting from Model Buidling

先给.dat补充几点

n=24;
 
x = [1,2,3,4,5,0.0, 0.5, 1.0, 1.5, 1.9, 2.5, 3.0, 3.5, 4.0, 4.5,
     5.0, 5.5, 6.0, 6.6, 7.0, 7.6, 8.5, 9.0, 10.0];
y = [10,20,30,40,50,1.0, 0.9, 0.7, 1.5, 2.0, 2.4, 3.2, 2.0, 2.7, 3.5,
     1.0, 4.0, 3.6, 2.7, 5.7, 4.6, 6.0, 6.8, 7.3];

然后 .mod 使用 MIP 和距离的绝对值

execute
{
  cplex.tilim=10;
}

int n=...;
range points=1..n;
float x[points]=...;
float y[points]=...;

int nbLines=2;

range lines=1..nbLines;

dvar float a[lines];
dvar float b[lines];

// distance between a point and a line
dvar float dist[points][lines];
// minimal distance to the closest line
dvar float dist2[points];

dvar float+ obj;
minimize obj; 

subject to
{
obj==sum(i in points) dist2[i];

forall(i in points,j in lines) dist[i][j]==abs(b[j]*x[i]+a[j]-y[i]);

forall(i in points) dist2[i]==min(l in lines ) dist[i][l];

}

// which line for each point ?
int whichline[p in points]=first({l | l in lines : dist2[p]==dist[p][l]});

execute
{

writeln("b = ",b);
writeln("a = ",a);
writeln("which line = ",whichline);
}

给予

b =  [0.6375 10]
a =  [0.58125 0]
which line =  [2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]

二次方程式

int n=...;
range points=1..n;
float x[points]=...;
float y[points]=...;

int nbLines=2;

range lines=1..nbLines;

dvar float a[lines];
dvar float b[lines];

dvar float distance[points][lines];
dvar boolean which[points]; // 1 means 1, 0 means 2

minimize sum(i in points,j in lines) distance[i][j]^2; 

subject to
{
forall(i in points,j in 0..1) (which[i]==j) => (distance[i][2-j]==b[2-j]*x[i]+a[2-j]-y[i]);

}



execute
{

writeln("b = ",b);
writeln("a = ",a);
writeln("which = ",which);
}

给予

b =  [0.61077 10]
a =  [0.42613 0]
which =  [0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]