思考: 从曲线中提取出近似直线的一段

        这个问题也是别人问我的, 我思考了一些时间, 希望抛砖引玉, 得到更好的方法.

        问题是这样的: 有一些离散的点, 在坐标系中把它们拟合成一条曲线, 其中有一段看上去很像是直线, 现在要求出这段"直线"的起始坐标和结束坐标, 并把这条线的方程求出来. 如下图:

2011082300583973.jpg

        从图中可以用肉眼看出标注的那一段近似一条直线, 问题是如果通过程序求出来.

我的想法

        我的想法是这样的, 假设这条线是存在的, 那么这条线里的点, 它们相邻两两之间Y之差应该是"相似"的, 具体相似要什么程度, 需要有一个"允许误差值". 在这个允许误差值下, 尽可能多的把这条曲线上连续的点拉拢到一条直线上, 最后根据这些点用最小二乘法就可以求得直线方程了.

获取这些连续的点:

static List<Point> GetSequential(List<Point> points, double allowLapse)
{
    var resultList = new List<List<Point>>();
    var currentResult = new List<Point>();

    currentResult.Add(points[0]);
    for (int i = 1; i < points.Count; i++)
    {
        //如果这个点与前一个点的高度差在一个合理的范围内
        if ((points[i].Y - points[i - 1].Y) < allowLapse)
            currentResult.Add(points[i]);
        else
        {
            resultList.Add(currentResult);
            currentResult = new List<Point>();
            currentResult.Add(points[i]);
        }
    }
    resultList.Add(currentResult);
    //从集合中选出点的数量最多的那组数据
    List<Point> result = resultList.OrderByDescending(list => list.Count).First();
    return result;
}

Point类的定义, 很简单:

internal class Point
{
    public double X { get; private set; }
    public double Y { get; private set; }

    public Point(double x, double y)
    {
        X = x; Y = y;
    }
}

根据一些点, 用最小二乘法求出直线:

static void LeastSquare(List<Point> points)
{
    double avgX = points.Average(point => point.X);
    double avgY = points.Average(point => point.Y);

    double numerator = points.Sum(point => (point.X - avgX) * (point.Y - avgY));
    double denominator = points.Sum(point => (point.X - avgX) * (point.X - avgX));
    double K = numerator / denominator; //斜率

    double X0 = avgY - K * avgX;
    //为了方便测试, 我直接在这里输出结果
    Console.WriteLine("Y={0} + {1}X\nStart ({2}, {3})\tEnd ({4}, {5})\nPoints Count: {6}", X0, K, points.First().X, points.First().Y, points.Last().X, points.Last().Y, points.Count);
}

测试结果

允许Y差值方程起点终点包含点数
0.005Y=0.27 + 0.45X(0.13, 0.328201)(0.15, 0.337173)3
0.010Y=0.25 + 0.60X(0.12, 0.318606)(0.17, 0.350823)6
0.015Y=0.48 + 1.08X(0.54, 1.060612)(0.77, 1.318348)24
0.020Y=0.23 + 1.46X(0.28, 0.611221)(0.77, 1.318348)50
0.025Y=0.22 + 1.48X(0.26, 0.56426)(0.77, 1.318348)52
0.030Y=0.13 + 1.64X(0.09, 0.255596)(0.77, 1.318348)69
0.035Y=0.13 + 1.64X(0.08, 0.222174)(0.77, 1.318348)70
0.040Y=0.12 + 1.65X(0.07, 0.183729)(0.77, 1.318348)71

        对比文中开头的插图, 可以看出"允许Y差值"在0.020和0.025, 得到的数据还是比较靠谱的. 但这样的做法还有一点问题, 如果每个点都比前一个点高一点(在允许Y差值范围内), 等到点积累多了, 从整体看, 这些点组成的线就是一条向上的抛物线了, 那么这个算法就存在问题.

       如果您有什么想法, 欢迎交流, 附上图中数据下载(数据只含Y坐标值, X从0.0开始, 相邻两点X间隔0.01).

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值