• 两种多项式插值方法


    泰勒公式表明许多复杂的三角函数、指数甚至对数方程都可以转换为多项式的形式,或在某一区间用多项式方程代替,多项式不但因为结构清晰简单,而且其各级导数连续、平滑且易于计算,所以在回归、曲线拟合、插值等领域普遍使用,工业机器人的路径规划、关节参数计算就是典型应用。

    插值(Interpolation)是在有限的离散数据之间补充一些数据,使这组离散数据符合某个连续函数,是数学计算中最基本和常用的手段,插值的方法很多,如埃尔米特(Hermite)、埃特金(Aitken)、阿克玛(Akima)等等,以前介绍的多项式最小二乘回归也可以视作多项式(Polynomial)的插值形式,其实各类样条曲线(Spline)也是插值的应用,这里主要介绍一些常用插值的程序实现。

    一、拉格朗日(Lagrange) 多项式插值

        拉格朗日多项式函数表示为:

    K为多项式的阶次;

    举例:由函数y = x3 生成数据集x:[1,3,5,7]   y:[1,27,125,343]

    K = 2时

    X:[1,3,5]区间:

    =9x2-23x+15

    X:[3,5,7]区间:

    =15x2-71x+105

    K = 3时

       /// <summary>
        /// Lagrange polynomial
        /// </summary>
        /// <param name="order">多项式阶次</param>
        /// <param name="x">x数据集</param>
        /// <param name="y">y数据集</param>
        /// <param name="xp">插值x</param>
        /// <param name="dy">一阶导数</param>
        /// <returns>插值y</returns>
        public static double Lagrange(int order, double[] x, double[] y, double xp,out double dy)
        {
            //            x = new double[] { 1, 3, 4, 7, 9, 10, 12, 15, 17 };
            //            y = new double[] { 0, 2, 15, 12, 5, 3, 1, 0, -1 };
            int len = x.Length;
            int k = 0;
            if (order < len)
            {
                while ((x[k] < xp) && k < len - 1)
                    k++;
                k -= order / 2;
                if (k < 0)
                    k = 0;
                if (k + order > len - 1)
                    k = len - order - 1;
            }
            else
            {
                order = len - 1;
                k = 0;
            }
            double nominator;
            double denominator;
            double yp = 0.0;
            dy = 0;
            for (int i = k; i <= k + order; i++)
            {
                nominator = 1.0;
                denominator = 1.0;
                double td = 0;
                for (int j = k; j <= k + order; j++)
                {
                    if (j != i)
                    {
                        nominator *= (xp - x[j]);
                        denominator *= (x[i] - x[j]);
                        double tdx = 1;
                        for (int m = k; m <= k + order; m++)
                        {
                            if (m != j&&m!=i)
                            { 
                                tdx *= (xp - x[m]);
                            }
                        }
                        td += tdx; 
                    }
                }
                if (denominator != 0.0)
                { 
                    yp += y[i] * nominator / denominator;
                    dy += y[i] * td / denominator;
                }
                else
                { 
                    yp = 0.0;
                    dy = 0;
                }
            }
            return yp;
        }

    一、牛顿(Newton)多项式插值

    牛顿多项式函数表示为:

     b0 = y0

    n0(x) = 1

    j>0: 

    n1 = (x-x0)

    n2 = (x-x0)(x-x1)

    n3 = (x-x0)(x-x1)(x-x2)

    K为多项式的阶次;

    举例:由函数y = x3 生成数据集x:[1,3,5,7]   y:[1,27,125,343]

    K = 2时

    X:[1,3,5]区间:

    X:[3,5,7]区间:

    K = 3时

        /// <summary>
        /// Newton Polynomial
        /// </summary>
        /// <param name="order">多项式阶次</param>
        /// <param name="x">x数据集</param>
        /// <param name="y">y数据集</param>
        /// <param name="xp">插值x</param>
        /// <param name="dy">一阶导数</param>
        /// <returns>插值y</returns>
        public static double NewTon(int order, double[] x, double[] y, double xp,out double dy)
        {
            //            x = new double[] { 1, 3, 4, 7, 9, 10, 12,15,17 };
            //            y = new double[] { 0, 2, 15, 12, 5, 3, 1,0 ,-1};
    
            int len = x.Length;
            int k = 0;
            if (order < len)
            {
                while ((x[k] < xp) && k < len - 1)
                    k++;
                k -= order / 2;
                if (k < 0)
                    k = 0;
                if (k + order > len - 1)
                    k = len - order - 1;
            }
            else
            {
                order = len - 1;
                k = 0;
            }
    
            //求多阶导数
            double[] f = new double[order + 1];
            f[0] = y[k];
            for (int i = 1; i <= order; i++)
            {
                double temY = 0;
                for (int j = 0; j <= i; j++)
                {
                    double tmpD = 0;
                    {
                        tmpD = y[j + k];
                        for (int n = 0; n <= i; n++)
                        {
                            if (n != j)
                                tmpD /= x[j + k] - x[n + k];
                        }
                    }
                    temY += tmpD;
                }
                f[i] = temY;
            }
            dy = 0;
            //  求Polynomial插值及一阶导数
            double tempYp = 0;
            double yp = f[0];
            for (int i = 1; i <= order; i++)
            {
                tempYp = f[i];
                double td = 0;
                for (int j = 0; j < i; j++)
                {
                    tempYp *= (xp - x[j + k]);
                    {
                        double tdx = 1;
                        for (int m = 0; m < i; m++)
                        {
                            if (m != j)
                                tdx *= (xp - x[m + k]);
                        }
                        td += tdx;
                    }
                }
                yp += tempYp;
                dy += f[i] * td;
            }
            return yp;
        }

     拉格朗日与牛顿多项式插值易于在临近边界的区域出现数据振荡,特别是阶次较高时,也称为龙格(Runge's Phenomenon)现象,但中间的数据平滑,并且计算量小,在实际使用中最好先进行图形化验证,根据插值数据动态选取计算数据,保证较好的拟合性能。

  • 相关阅读:
    sql 查询某个字段出现的次数
    Spark性能优化指导及总结
    数据结构与算法基础-排序
    数据仓库中数据模型之拉链表
    Hive over()窗口函数及应用实例
    dubbo 分布式服务框架
    netty 网络框架
    实现JavaScript继承
    【ThoughtWorks西安】澳洲业务线招聘大量C#开发工程师
    使用Docker搭建自己的GitLab服务
  • 原文地址:https://www.cnblogs.com/xrll/p/6000425.html
Copyright © 2020-2023  润新知