哈喽,我是小白~
在科学计算、数值分析与机器学习工程中,插值是将离散数据点延拓为连续函数的基础技术。
线性插值与多项式插值是两类典型方法,它们在原理、误差、稳定性、计算复杂度和应用场景上有本质差异。
设我们有一组有序的样本点:
目标是在一个区间(通常为 )上构造一个函数 ,使其满足插值条件:
-
线性插值通常指分段线性插值:在每个相邻节点区间 上,用一次多项式(直线)连接两点。 -
多项式插值通常指全局多项式插值:用一个次数为 的多项式(在 个点上恰好拟合)在整个区间内同时拟合所有点。
二者都是满足插值条件的构造,但它们的结构、误差性质与数值稳定性非常不同。
线性插值
分段线性插值的构造:
在每个区间 上,线性插值函数 定义为两点加权平均:
其中线性基函数为:
显然, 与 非负且和为1,在端点有 和 ,因此插值条件得以满足。
如果把所有区间拼接起来,就得到一个分段线性的连续函数 ,它在各节点处连续,但在节点处导数往往不连续(除非原数据恰好线性一致)。
向量形式与光滑性:
分段线性插值的全局表达是分片的:对于任意 ,只需找到其所在区间 ,再用上述局部线性公式。它属于局部支持的构造:每个节点只影响相邻区间,对整体的扰动影响有限。
光滑性方面, 是 连续,但一般不具有 (一阶导数)连续性,更不具备更高阶的光滑性。这使它更适合保形保界的场景(比如单调性维护),而不是需要平滑曲线的场合。
线性插值的误差公式与上界:
令真实函数为 ,假设 (至少在每个区间上二阶连续可导)。在单个区间 ,线性插值误差可写为:
该公式可用插值余项的方法(或Peano核、泰勒余项的特例)证明。由此得到误差上界:
由于在 上, 的最大值出现在中点 ,其值为 ,得到:
若网格大小定义为 ,则线性插值的区间误差量级为 ,在整体上若网格均匀 ,则全局插值误差为 。
这体现了分段线性插值的二阶收敛(相对于网格步长),尽管它只用一次多项式;这是很多工程场景中足够准确且稳定的一个重要原因。
多项式插值
多项式插值用一个次数为 的多项式 同时拟合所有 个节点:
它有多种等价表示,最常用的是拉格朗日型与牛顿型。
拉格朗日插值多项式:
拉格朗日型:
其中拉格朗日基函数为:
性质:
-
插值性: (Kronecker符号),因此 。 -
全局性:每个基函数在整个区间上都有支持(非局部),这使插值对每个点的变动都全局传递。
牛顿插值与差商:
牛顿形式把多项式写成分层结构:
其中系数 为差商。
定义递归差商:
-
一阶差商:
-
二阶差商:
-
一般地:
与牛顿形式对应的系数就是:
牛顿形式便于增量更新与高效求值(可用类似Horner的嵌套乘法),在实际数值实现中非常常见。
重心形式:
重心拉格朗日形式在实际插值计算中具有数值稳定性的优势(特别对大量点),其表达为:
其中权重:
该形式避免了直接计算拉格朗日多项式的乘积,有更好的稳定性和效率。
多项式插值的误差公式与Runge现象:
设 ,定义节点多项式:
则插值误差(余项)为:
从该公式可见:
-
当节点密集且 不大时,误差可较小; -
但如果节点选取不当(如在等距节点上用高次多项式插值某些函数),端点处 可能非常大,导致Runge现象:即插值在端点附近出现大幅振荡和发散。
一个经典例子是Runge函数:
用等距节点进行高次多项式插值会出现明显的端点振荡。而改用Chebyshev节点时,这种振荡显著缓和。
Lebesgue常数与稳定性:
插值算子对数据的扰动敏感度由Lebesgue常数刻画。定义插值函数空间的Lebesgue函数为:
Lebesgue常数为其上确界:
-
分段线性插值的基函数在各区间内非负且分片求和为1,其Lebesgue常数保持为 (或常数级),稳定性非常好。 -
全局多项式插值的Lebesgue常数对节点选择敏感。对于等距节点, 随 增长可能指数级变大,从而导致严重的放大噪声与数值不稳定;对于Chebyshev节点, 增长更慢(约为 ),稳定性显著改善。
完整案例
这里,我们使用PyTorch实现线性插值与牛顿多项式插值。
函数选用经典的Runge函数 ,节点选用等距节点以突出高次多项式插值的端点振荡与不稳定性。
import torch
import numpy as np
import matplotlib.pyplot as plt
torch.manual_seed(42)
np.random.seed(42)
# 真实函数(Runge函数)
def f_true(x):
return 1.0 / (1.0 + 25.0 * x**2)
# 等距节点
n = 15 # 节点数(n+1个点),多项式次数 n
x_nodes = torch.linspace(-1.0, 1.0, steps=n+1)
y_nodes = f_true(x_nodes)
# 牛顿差商计算系数
def newton_divided_differences(x, y):
# x,y: 1D tensors of length n+1
coef = y.clone().float()
m = x.shape[0]
for k in range(1, m):
# 更新从后往前以避免覆盖问题
for i in range(m-1, k-1, -1):
coef[i] = (coef[i] - coef[i-1]) / (x[i] - x[i-k])
return coef
# 牛顿形式求值(类似Horner的嵌套)
def newton_eval(coef, x_nodes, x_query):
# coef: length n+1
# x_nodes: length n+1
# x_query: tensor of arbitrary length
p = torch.zeros_like(x_query, dtype=torch.float32)
# 从最高阶开始嵌套乘法
p[:] = coef[-1]
for k in range(len(coef)-2, -1, -1):
p = coef[k] + (x_query - x_nodes[k]) * p
return p
# 分段线性插值(含边界线性外推)
def linear_interp(x_nodes, y_nodes, x_query):
# x_nodes must be sorted
# 对每个x_query找到所在区间索引
idx = torch.searchsorted(x_nodes, x_query, right=True) - 1
# 夹在有效区间范围内(0到n-1)
idx = torch.clamp(idx, 0, len(x_nodes)-2)
x0 = x_nodes[idx]
x1 = x_nodes[idx + 1]
y0 = y_nodes[idx]
y1 = y_nodes[idx + 1]
t = (x_query - x0) / (x1 - x0)
return (1.0 - t) * y0 + t * y1
# 预计算牛顿系数
coef_newton = newton_divided_differences(x_nodes, y_nodes)
# 评估区间内的高密度网格
x_fine = torch.linspace(-1.0, 1.0, steps=2000)
y_true_fine = f_true(x_fine)
# 线性插值与多项式插值(区间内部)
y_lin_fine = linear_interp(x_nodes, y_nodes, x_fine)
y_poly_fine = newton_eval(coef_newton, x_nodes, x_fine)
# 误差曲线
err_lin = torch.abs(y_true_fine - y_lin_fine)
err_poly = torch.abs(y_true_fine - y_poly_fine)
# 噪声敏感性分析:对节点y加入小噪声
noise_level = 0.02
y_nodes_noisy = y_nodes + noise_level * torch.randn_like(y_nodes)
# 重新计算带噪声的线性与多项式插值
coef_newton_noisy = newton_divided_differences(x_nodes, y_nodes_noisy)
y_lin_fine_noisy = linear_interp(x_nodes, y_nodes_noisy, x_fine)
y_poly_fine_noisy = newton_eval(coef_newton_noisy, x_nodes, x_fine)
# 与无噪声插值的差异(敏感性衡量)
delta_lin = y_lin_fine_noisy - y_lin_fine
delta_poly = y_poly_fine_noisy - y_poly_fine
# 外推行为:区间外评估
x_wide = torch.linspace(-1.5, 1.5, steps=2000)
y_true_wide = f_true(x_wide)
y_lin_wide = linear_interp(x_nodes, y_nodes, x_wide) # 线性外推:沿最后一段延伸
y_poly_wide = newton_eval(coef_newton, x_nodes, x_wide) # 多项式外推:可能快速发散
# 可视化
fig = plt.figure(figsize=(16, 12))
axs = fig.subplots(2, 2)
# 图1:拟合形态(区间内)
axs[0, 0].plot(x_fine.numpy(), y_true_fine.numpy(), color='black', lw=2.5, label='真实函数 f(x)')
axs[0, 0].scatter(x_nodes.numpy(), y_nodes.numpy(), color='red', s=45, label='节点(等距)')
axs[0, 0].plot(x_fine.numpy(), y_lin_fine.numpy(), color='lime', lw=2.0, label='线性插值')
axs[0, 0].plot(x_fine.numpy(), y_poly_fine.numpy(), color='magenta', lw=2.0, label='多项式插值(牛顿)')
axs[0, 0].set_title('图1:区间内拟合对比(Runge函数,等距节点)', fontsize=14)
axs[0, 0].set_xlabel('x')
axs[0, 0].set_ylabel('y')
axs[0, 0].legend(loc='best')
# 图2:误差曲线
axs[0, 1].plot(x_fine.numpy(), err_lin.numpy(), color='cyan', lw=2.0, label='|f - 线性|')
axs[0, 1].plot(x_fine.numpy(), err_poly.numpy(), color='orange', lw=2.0, label='|f - 多项式|')
axs[0, 1].set_title('图2:区间内绝对误差对比', fontsize=14)
axs[0, 1].set_xlabel('x')
axs[0, 1].set_ylabel('绝对误差')
axs[0, 1].legend(loc='best')
axs[0, 1].set_yscale('log')
# 图3:噪声敏感性(插值曲线对小噪声的响应)
axs[1, 0].plot(x_fine.numpy(), delta_lin.numpy(), color='blue', lw=2.0, label='线性插值变化(噪声)')
axs[1, 0].plot(x_fine.numpy(), delta_poly.numpy(), color='red', lw=2.0, label='多项式插值变化(噪声)')
axs[1, 0].axhline(0, color='black', lw=1.0)
axs[1, 0].set_title('图3:噪声敏感性对比(小噪声幅)', fontsize=14)
axs[1, 0].set_xlabel('x')
axs[1, 0].set_ylabel('插值变化量')
axs[1, 0].legend(loc='best')
# 图4:外推行为(区间外)
axs[1, 1].plot(x_wide.numpy(), y_true_wide.numpy(), color='black', lw=2.5, label='真实函数 f(x)')
axs[1, 1].plot(x_wide.numpy(), y_lin_wide.numpy(), color='green', lw=2.0, label='线性外推')
axs[1, 1].plot(x_wide.numpy(), y_poly_wide.numpy(), color='purple', lw=2.0, label='多项式外推')
axs[1, 1].axvline(-1.0, color='gray', ls='--', lw=1.0)
axs[1, 1].axvline(1.0, color='gray', ls='--', lw=1.0)
axs[1, 1].set_title('图4:外推行为对比([-1.5, 1.5])', fontsize=14)
axs[1, 1].set_xlabel('x')
axs[1, 1].set_ylabel('y')
axs[1, 1].legend(loc='best')
plt.tight_layout()
plt.show()
我们使用PyTorch进行数值计算与插值实现(牛顿差商与分段线性)。误差图使用对数尺度增强细节可视化。
噪声敏感性图显示在同一基准(无噪声插值)上的变化量,直观体现稳定性差异。
外推图中以虚线标记内插区间边界(-1与1),便于观察区间外行为。
可视化分析
区间内拟合对比(Runge函数,等距节点):
线性插值呈现分段直线的平滑连接,整体趋势跟随真实函数(黑色),在端点处较为稳健。
多项式插值在节点间可较好拟合,但在端点附近出现明显振荡(Runge现象),尤其在等距节点下尤为突出。
这直接体现了线性插值的稳健与保形和全局多项式插值在等距节点下的不稳定性。
区间内绝对误差对比(对数尺度):
线性插值误差整体在可控范围内,并且在区间内部并非显著恶化。
多项式插值误差在端点附近显著增大,甚至超过线性插值,说明高次多项式插值并不总是优于低阶分段方法。
这呼应了误差公式 :节点多项式 在端点附近大,误差易被放大。
噪声敏感性对比(小噪声):
线性插值变化幅度较小、平稳,说明线性插值对小扰动不敏感,具有稳定性。
多项式插值变化幅度显著、且呈现振荡,说明全局多项式插值对噪声极为敏感(高Lebesgue常数)。
这图强调全局性导致不稳定:一个节点的小变化影响整个区间的插值曲线。
外推行为对比(区间外[-1.5, 1.5]):
线性外推沿最后一段的斜率延伸,虽不精确,但行为可预期且不至于发散。
多项式外推在区间外快速偏离真实函数(黑色),甚至大幅发散,尤其在高次时更明显。
这图直观展示多项式外推不可靠,一般不建议使用高次多项式进行外推。
综合来看,线性插值的优点:稳健、局部性强、误差可控、抗噪声、外推行为不激进。
多项式插值的优点与缺点:理论逼近能力强,但对节点与噪声极为敏感,端点振荡明显、外推危险。
另外,线性插值与多项式插值既有联系又有显著差异:
-
从构造上,线性插值是分段局部、一次多项式的凸组合;多项式插值是全局高次多项式的全局拟合; -
从误差上,线性插值在每段有明确的二阶误差上界,整体稳健;多项式插值的误差与高阶导数与节点多项式相关,节点选择不当时会在端点处剧烈放大,产生Runge现象; -
从稳定性上,线性插值的Lebesgue常数常数级、抗噪声;多项式插值在等距节点下Lebesgue常数可能指数增长,对噪声高度敏感;
至此,大家应该可以直观且系统地理解了线性插值与多项式插值的核心区别与侧重点。实际实验中,遵循稳健优先、节点慎选、次数适中的原则,必要时优先使用分段插值(如线性或样条),如果是在追求高精度的场合配合合适节点与稳定的数值实现。
最后

