当前位置:   article > 正文

三次样条插值法

三次样条插值

一、问题引入

    对于给出如下的离散的数据点,现在想根据如下的数据点来推测x=5时的值,我们应该采用什么方法呢?

用于拟合样条函数的数据
x         f ( x)
3.02.5
4.51.0
7.02.5
9.00.5

   我们知道在平面上两个点确定一条直线,三个点确定一条抛物线(假设曲线的类型是抛物线),那么现在有四个点,我们很自然的会想到,既然两个点确定一条直线,那么最简单的方法就是,两个点之间连一条线,两个点之间连一条线,最后得到的一种折线图如下:这样我们只要确定x=5时的直线,把自变量的值带进去,就显然会得到预测的函数值。但是,这种方法显然具有很明显的缺陷:曲线不够光滑,连接点处的斜率变化过大。可能会导致函数的一阶导数不连续。那么我们应该如何解决这个问题呢?

   

二次样条的原理

   我们会想到既然直线不行,那么我们就用曲线来近似的代替和描述。最简单的曲线是二次函数,如果我们用二次函数:aX^2+bx+c来描述曲线,最后的结果可能会好一点,下图中一共有4个点,可以分成3个区间。每一个区间都需要一个二次函数来描述,一共需要9个未知数。下面的任务就是找出9个方程。

   如下图所示:一共有x0,x1,x2,x3四个点,三个区间,每个区间上都有一个方程。

   1>曲线方程在节点处的值必须相等,即函数在x1,x2两个点处的值必须符合两个方程,这里一共是4个方程:

       a1*x1^2+b1*x1+c1=f(x1)

       a2*x1^2+b2*x1+c2=f(x1)

       a2*x2^2+b2*x2+c2=f(x2)

       a3*x2^2+b3*x2+c3=f(x2)

   2>第一个端点和最后一个端点必须过第一个和最后一个方程:这里一共是2个方程

   3>节点处的一阶导数的值必须相等。这里为两个方程。

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

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

  4>在这里假设第一个方程的二阶导数为0:这里为一个方程:

          a1=0

 上面是对应的9个方程,现在只要把九个方程联立求解,最后就可以实现预测x=5处节点的值。

    

下面是写成矩阵的形式,由于a1=0,所以未知数的个数少了一个。


下面是二次样条的python实现,最后将结果绘制在图上。

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from pylab import mpl
  4. """
  5. 二次样条实现:
  6. 函数x的自变量为:3, 4.5, 7, 9
  7. 因变量为:2.5, 1 2.5, 0.5
  8. """
  9. x = [3, 4.5, 7, 9]
  10. y = [2.5, 1, 2.5, 0.5]
  11. """一共有三个区间,用二次样条求解,需要有9个方程"""
  12. """
  13. 功能:完后对二次样条函数求解方程参数的输入
  14. 参数:要进行二次样条曲线计算的自变量
  15. 返回值:方程的参数
  16. """
  17. def calculateEquationParameters(x):
  18. #parameter为二维数组,用来存放参数,sizeOfInterval是用来存放区间的个数
  19. parameter = []
  20. sizeOfInterval=len(x)-1;
  21. i = 1
  22. #首先输入方程两边相邻节点处函数值相等的方程为2n-2个方程
  23. while i < len(x)-1:
  24. data = init(sizeOfInterval*3)
  25. data[(i-1)*3]=x[i]*x[i]
  26. data[(i-1)*3+1]=x[i]
  27. data[(i-1)*3+2]=1
  28. data1 =init(sizeOfInterval*3)
  29. data1[i * 3] = x[i] * x[i]
  30. data1[i * 3 + 1] = x[i]
  31. data1[i * 3 + 2] = 1
  32. temp=data[1:]
  33. parameter.append(temp)
  34. temp=data1[1:]
  35. parameter.append(temp)
  36. i += 1
  37. #输入端点处的函数值。为两个方程,加上前面的2n-2个方程,一共2n个方程
  38. data = init(sizeOfInterval*3-1)
  39. data[0] = x[0]
  40. data[1] = 1
  41. parameter.append(data)
  42. data = init(sizeOfInterval *3)
  43. data[(sizeOfInterval-1)*3+0] = x[-1] * x[-1]
  44. data[(sizeOfInterval-1)*3+1] = x[-1]
  45. data[(sizeOfInterval-1)*3+2] = 1
  46. temp=data[1:]
  47. parameter.append(temp)
  48. #端点函数值相等为n-1个方程。加上前面的方程为3n-1个方程,最后一个方程为a1=0总共为3n个方程
  49. i=1
  50. while i < len(x) - 1:
  51. data = init(sizeOfInterval * 3)
  52. data[(i - 1) * 3] =2*x[i]
  53. data[(i - 1) * 3 + 1] =1
  54. data[i*3]=-2*x[i]
  55. data[i*3+1]=-1
  56. temp=data[1:]
  57. parameter.append(temp)
  58. i += 1
  59. return parameter
  60. """
  61. 对一个size大小的元组初始化为0
  62. """
  63. def init(size):
  64. j = 0;
  65. data = []
  66. while j < size:
  67. data.append(0)
  68. j += 1
  69. return data
  70. """
  71. 功能:计算样条函数的系数。
  72. 参数:parametes为方程的系数,y为要插值函数的因变量。
  73. 返回值:二次插值函数的系数。
  74. """
  75. def solutionOfEquation(parametes,y):
  76. sizeOfInterval = len(x) - 1;
  77. result = init(sizeOfInterval*3-1)
  78. i=1
  79. while i<sizeOfInterval:
  80. result[(i-1)*2]=y[i]
  81. result[(i-1)*2+1]=y[i]
  82. i+=1
  83. result[(sizeOfInterval-1)*2]=y[0]
  84. result[(sizeOfInterval-1)*2+1]=y[-1]
  85. a = np.array(calculateEquationParameters(x))
  86. b = np.array(result)
  87. return np.linalg.solve(a,b)
  88. """
  89. 功能:根据所给参数,计算二次函数的函数值:
  90. 参数:parameters为二次函数的系数,x为自变量
  91. 返回值:为函数的因变量
  92. """
  93. def calculate(paremeters,x):
  94. result=[]
  95. for data_x in x:
  96. result.append(paremeters[0]*data_x*data_x+paremeters[1]*data_x+paremeters[2])
  97. return result
  98. """
  99. 功能:将函数绘制成图像
  100. 参数:data_x,data_y为离散的点.new_data_x,new_data_y为由拉格朗日插值函数计算的值。x为函数的预测值。
  101. 返回值:空
  102. """
  103. def Draw(data_x,data_y,new_data_x,new_data_y):
  104. plt.plot(new_data_x, new_data_y, label="拟合曲线", color="black")
  105. plt.scatter(data_x,data_y, label="离散数据",color="red")
  106. mpl.rcParams['font.sans-serif'] = ['SimHei']
  107. mpl.rcParams['axes.unicode_minus'] = False
  108. plt.title("二次样条函数")
  109. plt.legend(loc="upper left")
  110. plt.show()
  111. result=solutionOfEquation(calculateEquationParameters(x),y)
  112. new_data_x1=np.arange(3, 4.5, 0.1)
  113. new_data_y1=calculate([0,result[0],result[1]],new_data_x1)
  114. new_data_x2=np.arange(4.5, 7, 0.1)
  115. new_data_y2=calculate([result[2],result[3],result[4]],new_data_x2)
  116. new_data_x3=np.arange(7, 9.5, 0.1)
  117. new_data_y3=calculate([result[5],result[6],result[7]],new_data_x3)
  118. new_data_x=[]
  119. new_data_y=[]
  120. new_data_x.extend(new_data_x1)
  121. new_data_x.extend(new_data_x2)
  122. new_data_x.extend(new_data_x3)
  123. new_data_y.extend(new_data_y1)
  124. new_data_y.extend(new_data_y2)
  125. new_data_y.extend(new_data_y3)
  126. Draw(x,y,new_data_x,new_data_y)

        二次样条函数运行之后的结果如下,从图像中,我们可以看出,二次样条在函数的连接处的曲线是光滑的。这时候,我们将x=5输入到函对应的函数端中,就可以预测相应的函数值。但是,这里还有一个问题,就是二次样条函数的前两个点是直线,而且函数的最后一个区间内看起来函数凸出很高。我们还想解决这些问题,这时候,我们想是否可以用三次样条函数来进行函数的模拟呢?

    

      三次样条的原理:

            三次样条的原理和二次样条的原理相同,我们用函数aX^3+bX^2+cX+d这个函数来进行操作,这里一共是4个点,分为3个区间,每个区间一个三次样条函数的话,一共是12个方程,只要我们找出这12个方程,这个问题就算解决了。

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

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

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

    4>两个函数在节点处的二阶导数应该相等,这里是两个方程。

    5>端点处的二阶导数为零,这里是两个方程。

           a1=0 

           b1=0

      三次样条的phthon实现

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from pylab import mpl
  4. """
  5. 三次样条实现:
  6. 函数x的自变量为:3, 4.5, 7, 9
  7. 因变量为:2.5, 1 2.5, 0.5
  8. """
  9. x = [3, 4.5, 7, 9]
  10. y = [2.5, 1, 2.5, 0.5]
  11. """
  12. 功能:完后对三次样条函数求解方程参数的输入
  13. 参数:要进行三次样条曲线计算的自变量
  14. 返回值:方程的参数
  15. """
  16. def calculateEquationParameters(x):
  17. #parameter为二维数组,用来存放参数,sizeOfInterval是用来存放区间的个数
  18. parameter = []
  19. sizeOfInterval=len(x)-1;
  20. i = 1
  21. #首先输入方程两边相邻节点处函数值相等的方程为2n-2个方程
  22. while i < len(x)-1:
  23. data = init(sizeOfInterval*4)
  24. data[(i-1)*4] = x[i]*x[i]*x[i]
  25. data[(i-1)*4+1] = x[i]*x[i]
  26. data[(i-1)*4+2] = x[i]
  27. data[(i-1)*4+3] = 1
  28. data1 =init(sizeOfInterval*4)
  29. data1[i*4] =x[i]*x[i]*x[i]
  30. data1[i*4+1] =x[i]*x[i]
  31. data1[i*4+2] =x[i]
  32. data1[i*4+3] = 1
  33. temp = data[2:]
  34. parameter.append(temp)
  35. temp = data1[2:]
  36. parameter.append(temp)
  37. i += 1
  38. # 输入端点处的函数值。为两个方程, 加上前面的2n - 2个方程,一共2n个方程
  39. data = init(sizeOfInterval * 4 - 2)
  40. data[0] = x[0]
  41. data[1] = 1
  42. parameter.append(data)
  43. data = init(sizeOfInterval * 4)
  44. data[(sizeOfInterval - 1) * 4 ] = x[-1] * x[-1] * x[-1]
  45. data[(sizeOfInterval - 1) * 4 + 1] = x[-1] * x[-1]
  46. data[(sizeOfInterval - 1) * 4 + 2] = x[-1]
  47. data[(sizeOfInterval - 1) * 4 + 3] = 1
  48. temp = data[2:]
  49. parameter.append(temp)
  50. # 端点函数一阶导数值相等为n-1个方程。加上前面的方程为3n-1个方程。
  51. i=1
  52. while i < sizeOfInterval:
  53. data = init(sizeOfInterval * 4)
  54. data[(i - 1) * 4] = 3 * x[i] * x[i]
  55. data[(i - 1) * 4 + 1] = 2 * x[i]
  56. data[(i - 1) * 4 + 2] = 1
  57. data[i * 4] = -3 * x[i] * x[i]
  58. data[i * 4 + 1] = -2 * x[i]
  59. data[i * 4 + 2] = -1
  60. temp = data[2:]
  61. parameter.append(temp)
  62. i += 1
  63. # 端点函数二阶导数值相等为n-1个方程。加上前面的方程为4n-2个方程。且端点处的函数值的二阶导数为零,为两个方程。总共为4n个方程。
  64. i = 1
  65. while i < len(x) - 1:
  66. data = init(sizeOfInterval * 4)
  67. data[(i - 1) * 4] = 6 * x[i]
  68. data[(i - 1) * 4 + 1] = 2
  69. data[i * 4] = -6 * x[i]
  70. data[i * 4 + 1] = -2
  71. temp = data[2:]
  72. parameter.append(temp)
  73. i += 1
  74. return parameter
  75. """
  76. 对一个size大小的元组初始化为0
  77. """
  78. def init(size):
  79. j = 0;
  80. data = []
  81. while j < size:
  82. data.append(0)
  83. j += 1
  84. return data
  85. """
  86. 功能:计算样条函数的系数。
  87. 参数:parametes为方程的系数,y为要插值函数的因变量。
  88. 返回值:三次插值函数的系数。
  89. """
  90. def solutionOfEquation(parametes,y):
  91. sizeOfInterval = len(x) - 1;
  92. result = init(sizeOfInterval*4-2)
  93. i=1
  94. while i<sizeOfInterval:
  95. result[(i-1)*2]=y[i]
  96. result[(i-1)*2+1]=y[i]
  97. i+=1
  98. result[(sizeOfInterval-1)*2]=y[0]
  99. result[(sizeOfInterval-1)*2+1]=y[-1]
  100. a = np.array(calculateEquationParameters(x))
  101. b = np.array(result)
  102. for data_x in b:
  103. print(data_x)
  104. return np.linalg.solve(a,b)
  105. """
  106. 功能:根据所给参数,计算三次函数的函数值:
  107. 参数:parameters为二次函数的系数,x为自变量
  108. 返回值:为函数的因变量
  109. """
  110. def calculate(paremeters,x):
  111. result=[]
  112. for data_x in x:
  113. result.append(paremeters[0]*data_x*data_x*data_x+paremeters[1]*data_x*data_x+paremeters[2]*data_x+paremeters[3])
  114. return result
  115. """
  116. 功能:将函数绘制成图像
  117. 参数:data_x,data_y为离散的点.new_data_x,new_data_y为由拉格朗日插值函数计算的值。x为函数的预测值。
  118. 返回值:空
  119. """
  120. def Draw(data_x,data_y,new_data_x,new_data_y):
  121. plt.plot(new_data_x, new_data_y, label="拟合曲线", color="black")
  122. plt.scatter(data_x,data_y, label="离散数据",color="red")
  123. mpl.rcParams['font.sans-serif'] = ['SimHei']
  124. mpl.rcParams['axes.unicode_minus'] = False
  125. plt.title("三次样条函数")
  126. plt.legend(loc="upper left")
  127. plt.show()
  128. result=solutionOfEquation(calculateEquationParameters(x),y)
  129. new_data_x1=np.arange(3, 4.5, 0.1)
  130. new_data_y1=calculate([0,0,result[0],result[1]],new_data_x1)
  131. new_data_x2=np.arange(4.5, 7, 0.1)
  132. new_data_y2=calculate([result[2],result[3],result[4],result[5]],new_data_x2)
  133. new_data_x3=np.arange(7, 9.5, 0.1)
  134. new_data_y3=calculate([result[6],result[7],result[8],result[9]],new_data_x3)
  135. new_data_x=[]
  136. new_data_y=[]
  137. new_data_x.extend(new_data_x1)
  138. new_data_x.extend(new_data_x2)
  139. new_data_x.extend(new_data_x3)
  140. new_data_y.extend(new_data_y1)
  141. new_data_y.extend(new_data_y2)
  142. new_data_y.extend(new_data_y3)
  143. Draw(x,y,new_data_x,new_data_y)
   三次样条函数运行结果如下:

   



本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/193321
推荐阅读
相关标签
  

闽ICP备14008679号