在工程计算和科学实验中,我们常常需要根据离散的数据点来构建连续的函数关系。多项式插值和拟合是解决这类问题的两种经典方法,它们虽然相似但有着本质区别。
多项式插值要求构造的n次多项式P(x)必须精确通过给定的n+1个数据点。从数学角度看,这相当于求解一个线性方程组:
code复制a₀ + a₁x₀ + a₂x₀² + ... + aₙx₀ⁿ = y₀
a₀ + a₁x₁ + a₂x₁² + ... + aₙx₁ⁿ = y₁
...
a₀ + a₁xₙ + a₂xₙ² + ... + aₙxₙⁿ = yₙ
这个范德蒙德方程组在x值互不相同时必有唯一解。在实际应用中,我们更常用拉格朗日插值或牛顿插值法,它们通过构造基函数的方式避免了直接求解方程组。
注意:当插值点数量较多时,高阶多项式会出现龙格现象(Runge's phenomenon),导致函数在区间端点附近剧烈振荡。因此实际应用中通常采用分段低次插值。
与插值不同,多项式拟合不要求曲线通过所有数据点,而是通过最小二乘法使残差平方和最小:
min Σ(yᵢ - P(xᵢ))²
这种方法特别适合处理带有测量误差的实验数据。拟合多项式的次数选择至关重要:
一个实用的判断方法是观察残差随次数增加的变化,当残差不再显著减小时停止增加次数。
MATLAB提供了完整的多项式处理工具链:
matlab复制% 多项式拟合(可指定任意次数)
p = polyfit(x, y, n);
% 多项式求值
y_pred = polyval(p, x_new);
% 多项式显示
polyout(p) % 需要Symbolic Math Toolbox
MATLAB的polyfit实际上使用QR分解求解最小二乘问题。对于n次多项式拟合m个数据点(m>n),其计算复杂度为O(mn²)。当m>>n时,建议先对数据进行归一化处理:
matlab复制x_normalized = (x - mean(x))/std(x);
[p, S] = polyfit(x_normalized, y, n);
这样可以提高数值稳定性,特别是当x的取值范围较大时。
虽然MATLAB没有内置拉格朗日插值函数,但我们可以高效实现:
matlab复制function yi = lagrange(x, y, xi)
n = length(x);
yi = zeros(size(xi));
for k = 1:n
% 计算拉格朗日基函数
L = ones(size(xi));
for j = [1:k-1 k+1:n]
L = L .* (xi - x(j))/(x(k) - x(j));
end
yi = yi + y(k)*L;
end
end
这个实现使用了向量化计算,可以同时处理多个插值点。对于大规模插值问题(n>20),建议改用分段插值或样条插值。
完成拟合后,我们需要评估模型质量:
matlab复制[y_pred, delta] = polyval(p, x, S); % 获取预测区间
% 计算R²
SS_res = sum((y - y_pred).^2);
SS_tot = sum((y - mean(y)).^2);
R2 = 1 - SS_res/SS_tot;
% 绘制预测区间
plot(x, y, 'bo', x_pred, y_pred, 'r-', ...
x_pred, y_pred+delta, 'g--', x_pred, y_pred-delta, 'g--');
R²越接近1表示拟合越好,但要注意这不能完全反映模型质量,还需结合残差分析。
假设我们有一组温度传感器的测试数据:
matlab复制temp = [20 25 30 35 40 45 50]; % 标准温度
reading = [20.1 24.8 30.2 35.1 39.9 45.3 49.8]; % 传感器读数
我们需要建立读数与实际温度的映射关系:
matlab复制% 二次多项式拟合
[p, S] = polyfit(reading, temp, 2);
% 评估拟合效果
xx = linspace(min(reading), max(reading), 100);
[yy, delta] = polyval(p, xx, S);
figure
plot(reading, temp, 'o', xx, yy, '-', xx, yy+delta, '--', xx, yy-delta, '--')
xlabel('传感器读数')
ylabel('实际温度')
title('温度传感器校准曲线')
分析某股票60天的收盘价:
matlab复制price = [100 102 99 101 105 ... ]; % 60天价格数据
days = 1:60;
% 尝试不同次数的多项式
figure
subplot(2,2,1)
plot(days, price, 'o-')
title('原始数据')
for k = 1:3
subplot(2,2,k+1)
p = polyfit(days, price, k);
plot(days, price, 'o', days, polyval(p, days), '-')
title(sprintf('%d次多项式拟合',k))
end
这个案例展示了如何通过可视化选择适当的拟合次数。
对于复杂的数据模式,可以采用分段拟合策略:
matlab复制% 将数据分为三段
break_points = [20, 40];
piecewise_p = cell(1,3);
% 对各段分别拟合
piecewise_p{1} = polyfit(days(1:20), price(1:20), 2);
piecewise_p{2} = polyfit(days(21:40), price(21:40), 3);
piecewise_p{3} = polyfit(days(41:end), price(41:end), 1);
% 绘制结果
figure
plot(days, price, 'o')
hold on
plot(1:20, polyval(piecewise_p{1}, 1:20), 'r-')
plot(21:40, polyval(piecewise_p{2}, 21:40), 'g-')
plot(41:60, polyval(piecewise_p{3}, 41:60), 'b-')
问题1:拟合曲线震荡剧烈
问题2:拟合效果不理想
问题3:预测区间过宽
对于大规模数据拟合:
matlab复制% 示例:使用parfor加速交叉验证
n_folds = 5;
cv_error = zeros(1, 10); % 评估1-10次多项式
parfor d = 1:10
errors = zeros(1, n_folds);
for k = 1:n_folds
% 划分训练/测试集
train_idx = setdiff(1:length(x), k:n_folds:length(x));
p = polyfit(x(train_idx), y(train_idx), d);
errors(k) = mean((y(k:n_folds:end) - polyval(p, x(k:n_folds:end))).^2);
end
cv_error(d) = mean(errors);
end
MATLAB还支持高维多项式拟合,例如二维曲面:
matlab复制% 生成测试数据
[x, y] = meshgrid(-2:0.5:2);
z = x.*exp(-x.^2 - y.^2) + 0.1*randn(size(x));
% 二次曲面拟合
sf = fit([x(:), y(:)], z(:), 'poly22');
% 可视化
plot(sf, [x(:), y(:)], z(:))
xlabel('X')
ylabel('Y')
zlabel('Z')
这种拟合可用于地理高程建模、温度场分析等场景。对于更复杂的曲面,可以考虑使用griddata进行插值或fit函数的高阶选项。