插值-样条插值

百度百科定义

插值:在离散数据的基础上插补连续函数,使得这条连续曲线经过全部离散点,同时也可以估计出函数在其他点的近似值。

样条插值:一种以 可变样条 来作出一条经过一系列点的光滑曲线的数学方法。插值样条是由一些多项式组成的,每一个多项式都是由相邻的两个数据点决定的,这样,任意的两个相邻的多项式以及它们的导数在连接点处都是连续的。

样条插值法

简单理解,就是每两个点之间确定一个函数,这个函数就是一个样条,函数不同,样条就不同,所以定义中说 可变样条,然后把所有样条分段结合成一个函数,就是最终的插值函数。

思路1 - 线性样条

两点确定一条直线,我们可以在每两点间画一条直线,就可以把所有点连起来。

显然曲线不够光滑,究其原因是因为连接点处导数不相同。

思路2 - 二次样条

直线不行,用曲线代替,二次函数是最简单的曲线。

假设4个点,x0,x1,x2,x3,有3个区间,需要3个二次样条,每个二次样条为  ax^2+bx+c,故总计9个未知数。

1. x0,x3两个端点都有一个二次函数经过,可确定2个方程

2. x1,x2两个中间点都有两个二次函数经过,可确定4个方程

3. 中间点处必须连续,需要保证左右二次函数一阶导相等

    2*a1*x1+b1=2*a2*x1+b2

   2*a2*x2+b2=2*a3*x2+b3

可确定2个方程,此时有了8个方程。

4. 这里假设第一方程的二阶导为0,即 a1=0,又是一个方程,共计9个方程。   【见补充】 

联立即可求解。

python 实现

# encoding:utf-8
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
"""
二次样条实现
"""
x = [3, 4.5, 7, 9]
y = [2.5, 1, 2.5, 0.5]

def calculateEquationParameters(x):
    #parameter为二维数组,用来存放参数,sizeOfInterval是用来存放区间的个数
    parameter = []
    sizeOfInterval=len(x)-1
    i = 1
    #首先输入方程两边相邻节点处函数值相等的方程为2n-2个方程
    while i < len(x)-1:
        data = init(sizeOfInterval*3)
        data[(i-1)*3]=x[i]*x[i]
        data[(i-1)*3+1]=x[i]
        data[(i-1)*3+2]=1
        data1 =init(sizeOfInterval*3)
        data1[i * 3] = x[i] * x[i]
        data1[i * 3 + 1] = x[i]
        data1[i * 3 + 2] = 1
        temp=data[1:]
        parameter.append(temp)
        temp=data1[1:]
        parameter.append(temp)
        i += 1
    #输入端点处的函数值。为两个方程,加上前面的2n-2个方程,一共2n个方程
    data = init(sizeOfInterval*3-1)
    data[0] = x[0]
    data[1] = 1
    parameter.append(data)
    data = init(sizeOfInterval *3)
    data[(sizeOfInterval-1)*3+0] = x[-1] * x[-1]
    data[(sizeOfInterval-1)*3+1] = x[-1]
    data[(sizeOfInterval-1)*3+2] = 1
    temp=data[1:]
    parameter.append(temp)
    #端点函数值相等为n-1个方程。加上前面的方程为3n-1个方程,最后一个方程为a1=0总共为3n个方程
    i=1
    while i < len(x) - 1:
        data = init(sizeOfInterval * 3)
        data[(i - 1) * 3] =2*x[i]
        data[(i - 1) * 3 + 1] =1
        data[i*3]=-2*x[i]
        data[i*3+1]=-1
        temp=data[1:]
        parameter.append(temp)
        i += 1
    return parameter

"""
对一个size大小的元组初始化为0
"""
def init(size):
    j = 0
    data = []
    while j < size:
        data.append(0)
        j += 1
    return data

"""
功能:计算样条函数的系数。
参数:parametes为方程的系数,y为要插值函数的因变量。
返回值:二次插值函数的系数。
"""

def solutionOfEquation(parametes,y):
    sizeOfInterval = len(x) - 1
    result = init(sizeOfInterval*3-1)
    i=1
    while i<sizeOfInterval:
        result[(i-1)*2]=y[i]
        result[(i-1)*2+1]=y[i]
        i+=1
    result[(sizeOfInterval-1)*2]=y[0]
    result[(sizeOfInterval-1)*2+1]=y[-1]
    a = np.array(calculateEquationParameters(x))
    b = np.array(result)
    return np.linalg.solve(a,b)

"""
功能:根据所给参数,计算二次函数的函数值:
参数:parameters为二次函数的系数,x为自变量
返回值:为函数的因变量
"""
def calculate(paremeters,x):
    result=[]
    for data_x in x:
        result.append(paremeters[0]*data_x*data_x+paremeters[1]*data_x+paremeters[2])
    return  result

"""
功能:将函数绘制成图像
参数:data_x,data_y为离散的点.new_data_x,new_data_y为由拉格朗日插值函数计算的值。x为函数的预测值。
返回值:空
"""
def  Draw(data_x,data_y,new_data_x,new_data_y):
        plt.plot(new_data_x, new_data_y, label=u"拟合曲线", color="black")
        plt.scatter(data_x,data_y, label=u"离散数据",color="red")
        mpl.rcParams[‘font.sans-serif‘] = [‘SimHei‘]
        mpl.rcParams[‘axes.unicode_minus‘] = False
        plt.title(u"二次样条函数")
        plt.legend(loc="upper left")
        plt.show()

result=solutionOfEquation(calculateEquationParameters(x),y)
new_data_x1=np.arange(3, 4.5, 0.1)
new_data_y1=calculate([0,result[0],result[1]],new_data_x1)
new_data_x2=np.arange(4.5, 7, 0.1)
new_data_y2=calculate([result[2],result[3],result[4]],new_data_x2)
new_data_x3=np.arange(7, 9.5, 0.1)
new_data_y3=calculate([result[5],result[6],result[7]],new_data_x3)
new_data_x=[]
new_data_y=[]
new_data_x.extend(new_data_x1)
new_data_x.extend(new_data_x2)
new_data_x.extend(new_data_x3)
new_data_y.extend(new_data_y1)
new_data_y.extend(new_data_y2)
new_data_y.extend(new_data_y3)
Draw(x,y,new_data_x,new_data_y)

可以看到 y 是多段二次函数拼接而成。

输出

二次样条插值连续光滑,看起来效果还行。

只是前两个点之间是条直线,因为假设a1=0,二次函数变成b1x+c1,显然是直线;

而且最后两个点之间过于陡峭 。

思路3 - 三次样条

二次函数最高项系数为0,导致变成直线,那三次函数最高项系数为0,还是曲线,插值效果应该更好。

三次样条思路与二次样条基本相同,

同样假设4个点,x0,x1,x2,x3,有3个区间,需要3个三次样条,每个三次样条为  ax^3+bx^2+cx+d,故总计12个未知数。

1. 内部节点处的函数值应该相等,这里一共是4个方程。

2. 函数的第一个端点和最后一个端点,应该分别在第一个方程和最后一个方程中。这里是2个方程。

3. 两个函数在节点处的一阶导数应该相等。这里是两个方程。

4. 两个函数在节点处的二阶导数应该相等,这里是两个方程。       【见补充】

5. 假设端点处的二阶导数为零,这里是两个方程。          【见补充】

a1=0

b1=0

python 实现

# encoding:utf-8
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl
"""
三次样条实现
"""
x = [3, 4.5, 7, 9]
y = [2.5, 1, 2.5, 0.5]

def calculateEquationParameters(x):
    #parameter为二维数组,用来存放参数,sizeOfInterval是用来存放区间的个数
    parameter = []
    sizeOfInterval=len(x)-1;
    i = 1
    #首先输入方程两边相邻节点处函数值相等的方程为2n-2个方程
    while i < len(x)-1:
        data = init(sizeOfInterval*4)
        data[(i-1)*4] = x[i]*x[i]*x[i]
        data[(i-1)*4+1] = x[i]*x[i]
        data[(i-1)*4+2] = x[i]
        data[(i-1)*4+3] = 1
        data1 =init(sizeOfInterval*4)
        data1[i*4] =x[i]*x[i]*x[i]
        data1[i*4+1] =x[i]*x[i]
        data1[i*4+2] =x[i]
        data1[i*4+3] = 1
        temp = data[2:]
        parameter.append(temp)
        temp = data1[2:]
        parameter.append(temp)
        i += 1
    # 输入端点处的函数值。为两个方程, 加上前面的2n - 2个方程,一共2n个方程
    data = init(sizeOfInterval * 4 - 2)
    data[0] = x[0]
    data[1] = 1
    parameter.append(data)
    data = init(sizeOfInterval * 4)
    data[(sizeOfInterval - 1) * 4 ] = x[-1] * x[-1] * x[-1]
    data[(sizeOfInterval - 1) * 4 + 1] = x[-1] * x[-1]
    data[(sizeOfInterval - 1) * 4 + 2] = x[-1]
    data[(sizeOfInterval - 1) * 4 + 3] = 1
    temp = data[2:]
    parameter.append(temp)
    # 端点函数一阶导数值相等为n-1个方程。加上前面的方程为3n-1个方程。
    i=1
    while i < sizeOfInterval:
        data = init(sizeOfInterval * 4)
        data[(i - 1) * 4] = 3 * x[i] * x[i]
        data[(i - 1) * 4 + 1] = 2 * x[i]
        data[(i - 1) * 4 + 2] = 1
        data[i * 4] = -3 * x[i] * x[i]
        data[i * 4 + 1] = -2 * x[i]
        data[i * 4 + 2] = -1
        temp = data[2:]
        parameter.append(temp)
        i += 1
    # 端点函数二阶导数值相等为n-1个方程。加上前面的方程为4n-2个方程。且端点处的函数值的二阶导数为零,为两个方程。总共为4n个方程。
    i = 1
    while i < len(x) - 1:
        data = init(sizeOfInterval * 4)
        data[(i - 1) * 4] = 6 * x[i]
        data[(i - 1) * 4 + 1] = 2
        data[i * 4] = -6 * x[i]
        data[i * 4 + 1] = -2
        temp = data[2:]
        parameter.append(temp)
        i += 1
    return parameter

"""
对一个size大小的元组初始化为0
"""
def init(size):
    j = 0
    data = []
    while j < size:
        data.append(0)
        j += 1
    return data

"""
功能:计算样条函数的系数。
参数:parametes为方程的系数,y为要插值函数的因变量。
返回值:三次插值函数的系数。
"""
def solutionOfEquation(parametes,y):
    sizeOfInterval = len(x) - 1
    result = init(sizeOfInterval*4-2)
    i=1
    while i<sizeOfInterval:
        result[(i-1)*2]=y[i]
        result[(i-1)*2+1]=y[i]
        i+=1
    result[(sizeOfInterval-1)*2]=y[0]
    result[(sizeOfInterval-1)*2+1]=y[-1]
    a = np.array(calculateEquationParameters(x))
    b = np.array(result)
    for data_x in b:
        print(data_x)
    return np.linalg.solve(a,b)

"""
功能:根据所给参数,计算三次函数的函数值:
参数:parameters为二次函数的系数,x为自变量
返回值:为函数的因变量
"""
def calculate(paremeters,x):
    result=[]
    for data_x in x:
        result.append(paremeters[0]*data_x*data_x*data_x+paremeters[1]*data_x*data_x+paremeters[2]*data_x+paremeters[3])
    return  result

"""
功能:将函数绘制成图像
参数:data_x,data_y为离散的点.new_data_x,new_data_y为由拉格朗日插值函数计算的值。x为函数的预测值。
返回值:空
"""
def  Draw(data_x,data_y,new_data_x,new_data_y):
        plt.plot(new_data_x, new_data_y, label=u"拟合曲线", color="black")
        plt.scatter(data_x,data_y, label=u"离散数据",color="red")
        mpl.rcParams[‘font.sans-serif‘] = [‘SimHei‘]
        mpl.rcParams[‘axes.unicode_minus‘] = False
        plt.title(u"三次样条函数")
        plt.legend(loc="upper left")
        plt.show()

result=solutionOfEquation(calculateEquationParameters(x),y)
new_data_x1=np.arange(3, 4.5, 0.1)
new_data_y1=calculate([0,0,result[0],result[1]],new_data_x1)
new_data_x2=np.arange(4.5, 7, 0.1)
new_data_y2=calculate([result[2],result[3],result[4],result[5]],new_data_x2)
new_data_x3=np.arange(7, 9.5, 0.1)
new_data_y3=calculate([result[6],result[7],result[8],result[9]],new_data_x3)
new_data_x=[]
new_data_y=[]
new_data_x.extend(new_data_x1)
new_data_x.extend(new_data_x2)
new_data_x.extend(new_data_x3)
new_data_y.extend(new_data_y1)
new_data_y.extend(new_data_y2)
new_data_y.extend(new_data_y3)
Draw(x,y,new_data_x,new_data_y)

输出

补充

微分连续性

s 代表三次样条,s‘是一阶导,s‘‘是二阶导

端点条件

上面我们对端点处的样条进行了假设,为什么呢?其实端点可以有多种不同的限制,常见有3种。

自由边界 Natural

首尾两端没有受到任何使他们弯曲的力,二次样条就是 s’=0,三次样条就是 s‘‘=0

固定边界 Clamped

首尾两端点的微分值被指定

非节点边界 Not-A-Knot

把端点当做中间点处理,三次函数不做假设,即

不同边界的比较

参考资料:

https://blog.csdn.net/flyingleo1981/article/details/53008931  三次样条插值(Cubic Spline Interpolation)及代码实现(C语言)

https://blog.csdn.net/deramer1/article/details/79034201  三次样条插值法

原文地址:https://www.cnblogs.com/yanshw/p/11194058.html

时间: 2024-10-12 02:34:47

插值-样条插值的相关文章

matlab 计算方法的总结

此文章是我的大学课程<计算方法>的总结,所选用的代码是matlab的形式,因为内容都是个人的总结,大部分都是只是一个函数+事例搞定..所以要问我基础的东西其实我也不是很懂.. 所以下面这文章不讲理论,,只讲函数怎么用..能力有限哈.. 目录 一元线性线性方程的求解 什么是一元线性方程,什么是一元非线性方程? 二分法 牛顿法 弦截法 线性方程的计算方法 相关命令的基础知识 高斯消去法 LT分解 插值法 线性插值 拉格朗日插值 牛顿插值 样条插值 其他命令 数值积分 牛顿-柯西公式 数值积分命令

插值与拟合

1.插值 -->求过已知有限个数据点的近似函数 1)拉格朗日多项式插值 -->n个插值点不同时确定了一个唯一的n次多项式 构造n次拉格朗日插值多项式(不使用解方程n个约束来求解待定系数) 2)牛顿插值 使用差商概念来构造牛顿插值公式(计算量小,余项与拉格朗日余项相等),当节点之差为常数时,使用差分来代替差商构造牛顿向前插值公式 3)分段线性插值 -->高次插值存在震荡缺陷,采用低次分段函数(线性函数) y=interp1(x0,y0,x,'method') -->method可取n

【数值分析】拉格朗日插值与牛顿插值

在工程应用和科学研究中,经常要研究变量之间的关系y=f(x).但对于函数f(x),常常得不到一个具体的解析表达式,它可能是通过观测或实验得到的一组数据(x,f(x)),x为一向量;或则是解析表达式非常复杂,不便于计算和使用.因此我们需要寻找一个计算比较简单的函数S(x)近似代替f(x),并使得S(x)=f(x),这种方法就称为插值法. 常用的插值法有: 一维插值法:拉格朗日插值.牛顿插值.分段低次插值.埃尔米特插值.样条插值. 二维插值法:双线性插值.双二次插值. 拉格朗日插值法 已知函数f(x

Matlab随笔之插值与拟合(上)

1.拉格朗日插值 新建如下函数: function y=lagrange(x0,y0,x) %拉格朗日插值函数 %n 个节点数据以数组 x0, y0 输入(注意 Matlat 的数组下标从1开始), %m 个插值点以数组 x 输入,输出数组 y 为 m 个插值 n=length(x0);m=length(x); for i=1:m z=x(i); s=0.0; for k=1:n p=1.0; for j=1:n if j~=k p=p*(z-x0(j))/(x0(k)-x0(j)); end

[数学建模(六)]使用MATLAB实现插值

常用的插值:拉格朗日多项式插值.牛顿插值.分段线性插值.Hermite 插值和三次样条插值. 1.拉格朗日插值法 function y=lagrange(x0,y0,x); n=length(x0);m=length(x); for i=1:m z=x(i); s=0.0; for k=1:n p=1.0; for j=1:n if j~=k p=p*(z-x0(j))/(x0(k)-x0(j)); end end s=p*y0(k)+s; end y(i)=s; end 测试: >> x0=

图像的降采样与升采样(二维插值)----转自LOFTER-gengjiwen

图像的降采样与升采样(二维插值) 1.先说说这两个词的概念: 降采样,即是采样点数减少.对于一幅N*M的图像来说,如果降采样系数为k,则即是在原图中 每行每列每隔k个点取一个点组成一幅图像.降采样很容易实现. 升采样,也即插值.对于图像来说即是二维插值.如果升采样系数为k,即在原图n与n+1两点之间插入k-1个点,使其构成k分.二维插值即在每行插完之后对于每列也进行插值. 插值的方法分为很多种,一般主要从时域和频域两个角度考虑.对于时域插值,最为简单的是线性插值.除此之外,Hermite插值,样

建模算法(八)&mdash;&mdash;插值与拟合

插值:求过已知有限个数据点的近似函数 拟合:已知有限个数据点,求近似函数,不要求过已知数据点,只要求在某种意义下在这些点的误差最小 (一)插值方法 一.拉格朗日多项式插值 1.插值多项式 就是做出一个多项式函数,经过给出的n个节点,并尽可能的接近原函数,将点带入多项式函数得到一个线性方程组 当系数矩阵满秩时,有唯一解.而,系数矩阵的行列式为 这是一个范德蒙德行列式,只要各个节点不同时,行列式就不为0,因此可得,一定能够解出系数方程 还有些指标 2.拉格朗日插值多项式 3.MATLAB实现 fun

插值与样条

先讲些题外话,前几天国庆回老家,在家中翻出了十年前大学时的一些教材课本,翻了几本看了看竟然如此的陌生.想当年考试前那么地刻苦学习,拼了命地上自——到如今变成了一场空,真令人唏嘘.其中有一本教材是<数值分析>,这门课也是挺难的,至少现在让我看是完全看不懂了.而<数值分析>一开始就是讲插值的,可以说插值是这门课的基础. 在离散数据的基础上补插连续函数,使得这条连续曲线通过全部给定的离散数据点.插值是离散函数逼近的重要方法,利用它可通过函数在有限个点处的取值状况,估算出函数在其他点处的近

插值相关总结

插值的通俗解释就是一种用一些已知的数据去预测想要的数据的方法. 多项式插值 多项式插值是最常见的一种函数插值(插值函数为多项式). $${p_n}(x) = {a_0} + {a_1}x + {a_2}{x^2} +  \cdots  + {a_n}{x^n}$$ 从几何上看可以理解为:已知平面上n+1个不同点,要寻找一条n次插值多项式函数$p(x)$通过曲线$f(x)$上已知的这n+1个点.使$p(x)$接近$f(x)$. 而将n个点代入多项式函数,则可用方程组表示,即 $$\left\{\b