样条函数

《数值分析》 David Kincaid, Ward Cheney 著, 王国荣 余耀明 徐兆亮 译.

因为看的论文里用到了样条函数,故查阅了一些资料并整理一下.

定义

样条函数是由一些具有某些连续性条件得子空间上得分段多项式构成. 给定n+1个点并且满足 这些点称为结点. 又假如指定一个整数, 具有结点的一个次样条函数是指满足下列条件的函数:

  1. 在每一个区间上, 是一个次数的多项式.
  2. 在上有阶连续导数.

三次样条

假设有数据. 我们来探讨如何唯一确定一个三次样条函数.

记上表示的多项式记为.
首先根据连续性条件有:

这是个方程,
再利用2阶连续可导的条件:

可知,这里有个方程,总共有个方程,但是唯一确定一个三次样条函数,需要个方程,所以还有2个自由度,这个怎么利用后面会提到.

定义, 因为容易证明, 且因为是三次多项式,所以其二阶导数为一阶多项式,所以:

其中.

进行俩次积分,其结果是:

其中是积分常数, 再利用可得:
\begin{array}{ll} S_i(x) &= \frac{z_i}{6h_i} (t_{i+1} - x)^3 + \frac{z_{i+1}}{6 h_i}(x-t_i)^3 \\ &+ (\frac{y_{i+1}}{h_i}-\frac{z_{i+1}h_i}{6})(x-t_i) + (\frac{y_i}{h_i} - \frac{z_ih_i}{6})(t_{i+1}-x). \end{array}
注: 原书在此处有误.

求在结点处的一阶导数:


上面俩式子相等,可得:

这个线性方程组有未知量, 方程个数为个,所以需要另外增添条件,一个很好的选择是(后面会有一个定理为其提供依据).

\left [ \begin{array}{cccccc} u_1 & h_1 & & & & \\ h_1 & u_2 & h_2 & & & \\ & h_2 & u_3 & h_3 & & \\ & & \ddots & \ddots & \ddots & \\ & & & h_{n-3} & u_{n-2} & h_{n-2} \\ & & & & h_{n-2} & u_{n-1} \end{array} \right] \left [ \begin{array}{c} z_1 \\ z_2 \\ z_3 \\ \vdots \\ z_{n-2} \\ z_{n-1} \end{array} \right ] = \left [ \begin{array}{c} v_1 \\ v_2 \\ v_3 \\ \vdots \\ v_{n-2} \\ v_{n-1} \end{array} \right ],
其中

三次样条函数代码



"""
三次样条函数实现
"""

import numpy as np
import matplotlib.pyplot as plt

class Polynomial:

    def __init__(self, t1, t2, y1, y2, z1, z2):
        assert t2 > t1, "t2 > t1 needed..."
        self.z = (z1, z2)
        self.y = (y1, y2)
        self.t = (t1, t2)
        self.h = t2 - t1

    def __call__(self, x):
        if x < self.t[0] or x > self.t[1]:
            return 0.
        reduce1_t2_x = self.t[1] - x
        reduce1_x_t1 = x - self.t[0]

        return self.z[0] / (6 * self.h) * reduce1_t2_x ** 3 + \
                self.z[1] / (6 * self.h) * reduce1_x_t1 ** 3 + \
               (self.y[1] / self.h - self.z[1] * self.h / 6) * reduce1_x_t1 + \
               (self.y[0] / self.h - self.z[0] * self.h / 6) * reduce1_t2_x


class Spline3:

    def __init__(self, t, y):
        self.n = len(t) - 1
        assert len(y) == self.n + 1, "t and y not match"
        self.t = np.array(t, dtype=float)
        self.y = np.array(y, dtype=float)
        self.h = np.diff(t)
        assert np.all(self.h > 0), "Invalid t"
        self.b = np.diff(y) * 6 / self.h
        self.calc_z()  #计算z
        self.generate_splines()  #生成分段多项式函数

    def calc_z(self):
        u = []
        v = []
        z = [0.]
        u.append((self.h[0] + self.h[1]) / 2)
        v.append(self.b[1] - self.b[0])
        for i in range(1, self.n-1): #对角化
            u_i = 2 * (self.h[i] + self.h[i-1]) - \
                  self.h[i-1] ** 2 / u[-1]
            v_i = self.b[i] - self.b[i-1] - \
                  self.h[i-1] * v[i-1] / u[-1]
            u.append(u_i)
            v.append(v_i)
        z.append(v[-1] / u[-1])
        for i in reversed(range(self.n-2)): #计算解
            z.append((v[i] - self.h[i] * z[-1]) / u[i])
        z.append(0.)
        z.reverse()
        self.z = z
        return z

    def generate_splines(self):
        s = []
        for i in range(self.n):
            t1 = self.t[i]
            t2 = self.t[i+1]
            y1 = self.y[i]
            y2 = self.y[i+1]
            z1 = self.z[i]
            z2 = self.z[i+1]
            s.append(Polynomial(t1, t2, y1,
                                y2, z1, z2))
        self.s = s
        return s

    def __call__(self, x):
        def f(x):
            if x < self.t[0] or x > self.t[-1]:
                return 0.
            for item in self.s:
                result = item(x)
                if result:
                    return result
            raise ValueError("Something wrong happened...")

        if not hasattr(x, "__len__"):
            return f(x)
        else:
            y = []
            for item in x:
                y.append(f(item))
            return np.array(y)


    def plot(self):
        t = np.linspace(self.t[0], self.t[-1], 100)
        y = self(t)
        fig, ax = plt.subplots()
        ax.plot(t, y, "b--")
        ax.set(xlabel="x", ylabel="y")
        ax.scatter(self.t, self.y, color="red")
        plt.show()

自然三次样条最优性定理

这个“最优性”似乎用“最光滑”来理解更为恰当吧. 这个定理也解释了之前的为什么.

定理1(自然三次样条最优性定理) 设在上连续且. 若是在节点上的自然三次样条插值,, 则:

证明: 令, 从而对于, 并且

只需证明:

注意到: , 以及在上是常数(记为), 我们有:
\begin{array}{ll} \int_a^b S'' g'' \mathrm{d}x &= \sum_{i=1}^n \int_{t_{i-1}}^{t_i} S'' g'' \mathrm{d}x \\ & = \sum_{i=1}^n \{ (S''g')|_{t_{i-1}}^{t_{i}}-\int_{t_{i-1}}^{t_{i}} S'''g'\mathrm{d}x\} \\ & = -\sum_{i=1}^n \int_{t_{i-1}}^{t_i} S''' g' \mathrm{d}x = - \sum_{i=1}^n c_i \int_{t_{i-1}}^{t_i} g' \mathrm{d}x \\ &= -\sum_{i=1}^n c_i [g(t_i) - g(t_{i-1})] = 0. \end{array}

实际上,条件可以放松,注意到在上述证明步骤中:


当这个表达式非负的时候,结论依然成立,即需要

例如: 和.

高次自然样条理论

自然样条: 一个次的自然样条是一个函数, 在每一个区间内,它都化为一个次数的多项式,而在区间和内化为一个次数至多为的多项式.

降在个结点上的全体次自然样条所构成的线性空间记为, 简记为.

下面不加证明地给出定理:

定理2(截断幂函数定理): 中地每个元有表达式

其中对于 否则为.

定理3(奇数次自然样条唯一性定理): 给定结点 设. 则存在唯一的次自然样条在这些结点上渠道给定的值.

证明很有趣,很在意奇数次的限制,这里给出证明.

证明:


假设,再结合定理2,只需下列方程组:

存在唯一解,这一个个未知量个方程的方程组,所以只需证明其对应的其次问题仅有零解即可. 假设. 下证:

其中. 利用分部积分可以得到:

其中最后一个等式成立的原因是. 重复上述操作可得:

因为是分段常值函数,所以:

由此,我们可知. 因此,是一个次数至多是次的多项式,但有个不同的零点,所以, 所以. 至此,唯一性得证.

考虑偶数次样条,我不知道是怎么定义的,也不想去查,假设是的类型,那么我们依然要证明(或者):

不然利用分部积分的时候,没法消项, 能顺利推导到:

此时已然是分段常值函数,则:

所以没办法证明其为0. 更不必证明,因为没法保证等.

如果是类型的,则需要证明:

可以顺利推导到:

显然也没法往下推.

定理4(奇数次自然样条最优性定理): 设及 假设是在结点上插值的次自然样条,则

B样条

这一节专门讨论样条函数系统,使得其他所有样条函数都可以由它的线性组合得到. 这些样条构成某些样条空间的基. 所以称为样条. 一旦给定结点,样条就很容易通过递归关系产生. 而且算法也比较简单. 样条以其优美的理论和数值计算中的典型性质著称. 此外,杨i套还可以得到进一步的推广.

我们在无限集上讨论:

但是,在后面,我们会发现,在实际使用中,我们都只会用到有限个结点,在无限集上只是便于讨论.

首先给出0次样条的定义:

容易得到.

高次样条由递归定义:

若令


B 样条的性质

引理1(B样条的支撑引理): 若并且, 则.

引理2(B样条正性引理): 设. 若, 则.

引理3(B样条的递归关系引理): 对一切, 我们有

引理4(B样条单位分解引理):对一切, 我们有

B样条的导数和积分

记, 有

引理5(B样条导数引理): 对于, B样条函数的导数可如下计算:

引理6(B样条光滑性引理): 对于, B 样条属于连续函数类.

从与B样条导数相关的引理,可得:

引理7(B样条积分引理) B样条函数的积分可如下计算:

附加性质

引理8(B样条线性无关性引理): B 样条集合在上线性无关.

引理9(B样条线性无关性引理): B样条集合在上线性无关.

记由在区间上的次样条函数所构成线性空间为.

定理1(空间的基定理): 空间的一个基是

从而,的维数是.

插值矩阵A的定义:

引理1 (插值矩阵引理1): 若插值矩阵非奇异,则.

引理2(插值矩阵引理2): 若并且对于有, 则非奇异.

定理2 (Schoenberg-Whitney定理): 设. 已知,则矩阵非奇异当且仅当其主对角线上不含零元.

定理3(B样条插值定理): 若中的结点满足

则能用样条空间插值这组节点上的任意一组数据.

注意到,如果我们的插值结点恰好就是, 那么仅有个方程,还有个自由度,此时方程的解是不唯一的。

你可能感兴趣的:(样条函数)