样条函数

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

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

定义

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

  1. 在每一个区间[t_{i-1}, t_i]上, S是一个次数\le k的多项式.
  2. [t_0, t_n]S(k-1)阶连续导数.

三次样条

假设有数据(t_0, y_0), (t_1, y_1), \ldots, (t_n, y_n). 我们来探讨如何唯一确定一个三次样条函数.

[t_i, t_{i+1}]上表示S的多项式记为S_i.
首先根据连续性条件有:
S_{i-1}(t_i)=y_i=S_{i}(t_i) \quad (1 \le i \le n-1), \\ S_0(t_0)=y_0, S_n(t_n)=y_n.
这是2n个方程,
再利用2阶连续可导的条件:
S'_{i-1}(t_i)=S'_i(t_i) \quad (1 \le i \le n-1) \\ S''_{i-1}(t_i)=S''_i(t_i) \quad (1 \le i \le n-1) \\
可知,这里有2n-2个方程,总共有4n-2个方程,但是唯一确定一个三次样条函数,需要4n个方程,所以还有2个自由度,这个怎么利用后面会提到.

定义z_i=S''(t_i), 因为容易证明S_i''(t_i)=z_i, S_i''(t_{i+1})=z_{i+1}, 且因为S_i是三次多项式,所以其二阶导数为一阶多项式,所以:
S_i''(x) = \frac{z_i}{h_i}(t_{i+1}-x) + \frac{z_{i+1}}{h_i}(x-t_i)
其中h_i := t_{i+1} - t_i.

进行俩次积分,其结果是S_i:
S_i(x) = \frac{z_i}{6h_i} (t_{i+1} - x)^3 + \frac{z_{i+1}}{6 h_i}(x-t_i)^3 + C(x-t_i)+D(t_{i+1}-x).
其中C, D是积分常数, 再利用S_i(t_i)=y_i, S_i(t_{i+1})=y_{i+1}可得:
\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}
注: 原书在此处有误.

求在结点处的一阶导数:
S_i'(t_i) = -\frac{h_i}{3} z_i - \frac{h_i}{6}z_{i+1} - \frac{y_i}{h_i} + \frac{y_{i+1}}{h_i},
S_{i-1}'(t_i) = \frac{h_{i-1}}{6} z_{i-1} + \frac{h_{i-1}}{3}z_i - \frac{y_{i-1}}{h_{i-1}}+\frac{y_i}{h_{i-1}}.
上面俩式子相等,可得:
h_{i-1}z_{i-1} + 2(h_i + h_{i-1})z_i + h_i z_{i+1} = \frac{6}{h_i}(y_{i+1} - y_i) - \frac{6}{h_{i-1}}(y_i - y_{i-1}), \: i=1,2,\ldots, n-1.

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

\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 ],
其中
h_i = t_{i+1} - t_i \\ u_i = 2(h_i + h_{i-1}) \\ b_i = \frac{6}{h_i} (y_{i+1} - y_i)\\ v = b_i - b_{i-1}.

三次样条函数代码



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

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()

自然三次样条最优性定理

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

定理1(自然三次样条最优性定理)f''[a, b]上连续且a=t_0<t_1<\cdots<t_n=b. 若Sf在节点t_i上的自然三次样条插值,0\le i \le n, 则:
\int_a^b [S''(x)]^2 \mathrm{d}x \le \int_a^b [f''(x)]^2 \mathrm{d}x.

证明: 令g \equiv f-S, 从而对于0 \le i \le n, g(t_i)=0并且
\int_a^b (f'')^2 \mathrm{d}x = \int_a^b (S'')^2 \mathrm{d}x + \int_a^b (g'')^2 \mathrm{d}x + 2 \int_a^b S'' g'' \mathrm{d}x,
只需证明:
\int_a^b S''g'' \mathrm{d}x \ge 0.

注意到: S''(t_0) = S''(t_n)=0, 以及在[t_{i-1}, t_i]S'''是常数(记为c_i), 我们有:
\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}

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

\sum_{i=1}^n (S''g')|_{t_{i-1}}^{t_i} = (S''g')(b) - (S''g')(a).
当这个表达式非负的时候,结论依然成立,即需要
S''(b)[f'(b)-S'(b)] \ge S''(a)[f'(a) - S'(a)].
例如: S'(a)=f'(a)S'(b) = f'(b).

高次自然样条理论

自然样条: 一个2m+1次的自然样条是一个函数S \in C^{2m}(\R), 在每一个区间[t_0, t_1], \cdots, [t_{n-1},t_n]内,它都化为一个次数\le 2m+1的多项式,而在区间(-\infty,t_0)(t_n, +\infty)内化为一个次数至多为m的多项式.

降在n+1个结点t_0, t_1, \ldots, t_n上的全体2m+1次自然样条所构成的线性空间记为\mathcal{N}^{2m+1}(t_0,t_1,\cdots,t_n), 简记为\mathcal{N}_n^{2m+1}.

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

定理2(截断幂函数定理): \mathcal{N}_n^{2m+1}中地每个元有表达式
S(x) = \sum_{i=1}^m a_ix^i + \sum_{i=0}^n b_i (x-t_i)_+^{2m+1},
其中对于0 \le j \le m, \sum_{i=0}^n b_it_I^j=0. (x-t_i)_+=x-t_i, \: x-t_i\ge0, 否则为0.

定理3(奇数次自然样条唯一性定理): 给定结点t_0 < t_1 < \cdots < t_n,0 \le m \le n. 则存在唯一的2m+1次自然样条在这些结点上渠道给定的值.

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

证明:

S(x) = \sum_{i=1}^m a_ix^i + \sum_{i=0}^n b_i (x-t_i)_+^{2m+1},
假设S(t_i)=\lambda_i,再结合定理2,只需下列方程组:
\left \{ \begin{array}{ll} S(t_i)= \lambda_i & 0 \le i \le n \\ \sum_{j=0}^n b_jt_j^i=0 & 0 \le i \le m \end{array} \right. ,
存在唯一解,这一个m+n+2个未知量m+n+2个方程的方程组,所以只需证明其对应的其次问题仅有零解即可. 假设\lambda_i=0, 0 \le i \le n. 下证:
I \equiv \int_a^b [S^{(m+1)}(x)]^2 \mathrm{d}x=0,
其中a=t_0,b=t_n. 利用分部积分可以得到:
I = S^{(m+1)}(x) S^{(m)}(x)|_a^b - \int_a^b S^{(m)}(x)S^{(m+2)}(x)\mathrm{d}x=-\int_a^b S^{(m)}(x)S^{(m+2)}(x)\mathrm{d}x,
其中最后一个等式成立的原因是S^{(k)}(a)=S^{(k)}(b)=0, k>m. 重复上述操作可得:
I = (-1)^m \int_a^b S^{(1)}(x)S^{(2m+1)}(x) \mathrm{d}x.
因为S^{(2m+1)}(x)是分段常值函数,所以:
I = (-1)^m \sum_{i=1}^n\int_{t_{i-1}}^{t_i} c_i S'(x) \mathrm{d}x = (-1)^m \sum_{i=1}^nc_i[S(t_i)-S(t_{i-1})]=0.
由此,我们可知S^{(m+1)} \equiv 0. 因此,S是一个次数至多是m次的多项式,但Sn+1>m个不同的零点,所以S=0, 所以a_i, b_i=0. 至此,唯一性得证.

考虑偶数次样条,我不知道是怎么定义的,也不想去查,假设是2m, m的类型,那么我们依然要证明(或者m+2,m+3,\ldots):
I \equiv \int_a^b [S^{(m+1)}(x)]^2 \mathrm{d}x=0,
不然利用分部积分的时候,没法消项, 能顺利推导到:
I = (-1)^{m-1} \int_a^b S^{(2)}(x)S^{(2m)}(x) \mathrm{d}x.
此时S^{(2m)}已然是分段常值函数,则:
I = (-1)^{m-1} \sum_{i=1}^n\int_{t_{i-1}}^{t_i} c_i S''(x) \mathrm{d}x = (-1)^{m-1} \sum_{i=1}^nc_i[S'(t_i)-S'(t_{i-1})].
所以没办法证明其为0. m+2, m+3, \ldots更不必证明,因为没法保证n+1>m+1等.

如果是2m,m-1类型的,则需要证明:
I \equiv \int_a^b [S^{(m)}(x)]^2 \mathrm{d}x=0,
可以顺利推导到:
I = (-1)^{m-1} \int_a^b S^{(1)}(x)S^{(2m-1)}(x) \mathrm{d}x.
显然也没法往下推.

定理4(奇数次自然样条最优性定理):m\le nf \in C^{m+1}[a,b]. 假设S是在结点a=t_0<t_1<\cdots<t_n=b上插值f2m+1次自然样条,则
\int_a^b [S^{(m+1)}(x)]^2 \mathrm{d}x \le \int_a^b [f^{(m+1)}(x)]^2 \mathrm{d}x.

B样条

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

我们在无限集上讨论:
\cdots < t_{-2} < t_{-1}<t_0 < t_1 <t_2 < \cdots
但是,在后面,我们会发现,在实际使用中,我们都只会用到有限个结点,在无限集上只是便于讨论.

首先给出0次B样条的定义:
B^0_i (x) = \left \{ \begin{array}{ll} 1 & x \in [t_i, t_{i+1}) \\ 0 & else \end{array} \right. .
容易得到\sum_{i=-\infty}^{\infty} B_i^0(x)=1.

高次B样条由递归定义:
B_i^k(x) = (\frac{x-t_i}{t_{i+k}-t_i})B_i^{k-1}(x)+(\frac{t_{i+k+1}-x}{t_{i+k+1}-t_{i+1}})B^{k-1}_{i+1}(x) \quad (k\ge 1).
若令
V_i^k (x) = \frac{x-t_i}{t_{i+k}-t_i},

B_i^k = V_i^k B_i^{k-1} + (1-V_{i+1}^k)B^{k-1}_{i+1}.

B 样条的性质

引理1(B样条的支撑引理):k\ge1并且x \not \in (t_i, t_{i+k+1}), 则B_i^k(x)=0.

引理2(B样条正性引理):k\ge 0. 若x \in (t_i, t_{i+k+1}), 则B_i^k(x)> 0.

引理3(B样条的递归关系引理): 对一切k\ge 0, 我们有
\sum_{i=-\infty}^{\infty} c_iB_i^k = \sum_{i=-\infty}^{\infty} [c_iV_i^k+c_{i-1}(1-V_i^k)]B_i^{k-1}.

引理4(B样条单位分解引理):对一切k \ge 0, 我们有
\sum_{i=-\infty}^{\infty} B_i^k(x) = 1.

B样条的导数和积分

\alpha_i^k = \frac{1}{t_{i+k}-t_i}, 有
\frac{\mathrm{d}}{\mathrm{d}x} V_i^k(x) = \alpha_i^k \\ \alpha_i^k V_i^{k+1} = \alpha_i^{k+1}V_i^k \\ \alpha_{i+1}^k (1-V_i^{k+1})=\alpha_i^{k+1} (1-V_{i+1}^k).

引理5(B样条导数引理): 对于k\ge 2, B样条函数的导数可如下计算:
\frac{\mathrm{d}}{\mathrm{d}x} B_i^k(x) = (\frac{k}{t_{i+1}-t_i}) B_i^{k-1}(x) - (\frac{k}{t_{i+k+1}-t_{i+1}})B_{i+1}^{k-1}(x).

引理6(B样条光滑性引理): 对于k \ge 1, B 样条B_i^k属于连续函数类C^{k-1}(\R).

从与B样条导数相关的引理,可得:
\frac{\mathrm{d}}{\mathrm{d}x} \sum_{i=-\infty}^{\infty} c_i B_i^k(x) = k \sum_{i=-\infty}^{\infty} (\frac{c_i-c_{i-1}}{t_{i+1}-t_i})B_i^{k-1}(x) \quad (k \ge 2).
引理7(B样条积分引理) B样条函数的积分可如下计算:
\int_{-\infty}^x B_i^k(s) \mathrm{d}s = (\frac{t_{i+k+1}-t_i}{k+1})\sum_{j=i}^{\infty}Bj^{k+1}(x).

附加性质

引理8(B样条线性无关性引理): B 样条集合\{B_j^k, B_{j+1}^k, \cdots, B_{j+k}^k\}(t_{k+j},t_{k+j+1})上线性无关.

引理9(B样条线性无关性引理): B样条集合\{B_{-k}^k, B_{-k+1}^k, \cdots, B_{n-1}^k\}(t_0, t_n)上线性无关.

记由在区间[t_0, t_1], [t_1, t_2], \cdots, [t_{n-1}, t_n]上的k次样条函数所构成线性空间为\mathcal{S}_n^k.

定理1(空间\mathcal{S}_n^k的基定理): 空间\mathcal{S}_n^k的一个基是
\{ B_i^k| [t_0, t_n]:-k\le i \le n-1\}
从而,\mathcal{S}_n^k的维数是k+n.

插值矩阵A的定义:
A_{ij} = B_j^k(x_i) \quad (i \le i,j \le n).

引理1 (插值矩阵引理1): 若插值矩阵非奇异,则A_{ii} \not = 0, \: 1 \le i \le n.

引理2(插值矩阵引理2):k=1并且对于1 \le i \le nt_i < x_i < t_{i+2}, 则A非奇异.

定理2 (Schoenberg-Whitney定理):x_1 < x_2 \cdots < x_n. 已知A_{ij}=B_j^k(x_i),则矩阵A非奇异当且仅当其主对角线上不含零元.

定理3(B样条插值定理):[t_0,t_n]中的结点x_1 , x_2, \cdots, x_{n+k}满足
t_{i-k-1}< x_i < t_i \quad (1 \le i \le n+k),
则能用样条空间\mathcal{S}_n^k插值这组节点上的任意一组数据.

注意到,如果我们的插值结点恰好就是t_0, \cdots, t_n, 那么仅有n+1个方程,还有k-1个自由度,此时方程的解是不唯一的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,635评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,543评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,083评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,640评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,640评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,262评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,833评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,736评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,280评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,369评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,503评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,870评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,340评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,460评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,909评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,512评论 2 359

推荐阅读更多精彩内容