用Matlab攻克数值计算难题:同济教材习题实战指南
刚接触数值计算时,我总被那些看似简单的数学公式弄得晕头转向——明明理论课上听懂了,一到课后习题就无从下手。直到发现Matlab这个神器,才真正打通了从理论到实践的任督二脉。本文将带你用Matlab破解同济《现代数值计算》中的典型难题,从线性方程组到数值积分,每个案例都配有可运行的完整代码和逐行解析。
1. 环境准备与基础操作
工欲善其事,必先利其器。在开始解题前,我们需要配置好Matlab环境并掌握几个核心操作技巧。不同于教材上的纯数学推导,实际编程中会遇到各种"坑",比如矩阵维度不匹配、算法收敛性判断等。
安装最新版Matlab后(建议R2020b及以上版本),先运行以下命令检查基础环境:
matlab复制% 检查Matlab版本和工具箱
ver
% 特别确认是否有以下工具箱
license('test','symbolic_toolbox')
数值计算中经常用到的几个关键函数:
format long:显示更多小数位cond(A):计算矩阵条件数norm(x):向量范数计算tic/toc:代码计时
提示:在脚本开头添加
clear; clc; close all;是个好习惯,可以避免之前运行的变量干扰当前工作区。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 线性方程组的实战求解
同济教材第二章的习题常出现各种病态方程组,这正是检验我们编程能力的好机会。以习题2.5为例,要求解如下Hilbert矩阵方程组:
code复制Hx = b,其中H是5阶Hilbert矩阵,b=[1,1,...,1]'
2.1 直接法实现
先看直接解法中的列主元高斯消元法。虽然Matlab内置的\运算符已经优化得很好,但理解底层实现很重要:
matlab复制function x = gauss_elimination(A, b)
[n, ~] = size(A);
Ab = [A, b]; % 增广矩阵
% 消元过程
for k = 1:n-1
% 列主元选择
[~, p] = max(abs(Ab(k:n, k)));
p = p + k - 1;
Ab([k p], :) = Ab([p k], :);
% 消元
for i = k+1:n
factor = Ab(i,k)/Ab(k,k);
Ab(i,k:n+1) = Ab(i,k:n+1) - factor*Ab(k,k:n+1);
end
end
% 回代
x = zeros(n,1);
for k = n:-1:1
x(k) = (Ab(k,n+1) - Ab(k,k+1:n)*x(k+1:n))/Ab(k,k);
end
end
运行测试:
matlab复制n = 5;
H = hilb(n); % 生成Hilbert矩阵
b = ones(n,1);
x_direct = gauss_elimination(H, b);
disp(['条件数:', num2str(cond(H))]);
disp('解向量:'); disp(x_direct');
2.2 迭代法对比
对于大型稀疏矩阵,迭代法更高效。实现经典的Jacobi迭代:
matlab复制function x = jacobi(A, b, tol, max_iter)
D = diag(diag(A));
L = -tril(A,-1);
U = -triu(A,1);
BJ = D\(L+U); % 迭代矩阵
f = D\b;
x = zeros(size(b));
for k = 1:max_iter
x_new = BJ*x + f;
if norm(x_new - x) < tol
break;
end
x = x_new;
end
end
比较两种方法的误差和耗时:
matlab复制% 生成一个对角占优矩阵测试
A = diag(3*ones(n,1)) + diag(ones(n-1,1),1) + diag(ones(n-1,1),-1);
x_jacobi = jacobi(A, b, 1e-6, 1000);
% 误差对比
disp(['直接法误差:', num2str(norm(A\x_direct - b))]);
disp(['Jacobi迭代误差:', num2str(norm(A\x_jacobi - b))]);
3. 插值问题的Matlab实现
第三章的插值问题看似简单,但选择合适的插值方法和节点分布至关重要。我们以习题3.7为例,要求在区间[-5,5]上对Runge函数f(x)=1/(1+x²)进行多项式插值。
3.1 等距节点插值
先尝试等距节点,观察Runge现象:
matlab复制f = @(x) 1./(1+x.^2);
x_interval = linspace(-5,5,11); % 11个等距节点
y_interval = f(x_interval);
% 构造插值多项式
xx = linspace(-5,5,1000);
yy_interp = interp1(x_interval, y_interval, xx, 'spline');
figure;
plot(xx, f(xx), 'b-', 'LineWidth', 1.5); hold on;
plot(xx, yy_interp, 'r--', 'LineWidth', 1.5);
scatter(x_interval, y_interval, 50, 'filled');
legend('真实函数','样条插值','插值节点');
title('等距节点插值(11点)');
3.2 Chebyshev节点优化
改用Chebyshev节点可显著改善振荡问题:
matlab复制n = 10; % 10次多项式
k = 0:n;
x_cheb = 5*cos((2*k+1)*pi/(2*(n+1))); % Chebyshev节点
y_cheb = f(x_cheb);
% 使用重心插值公式
w = (-1).^k .* sin((2*k+1)*pi/(2*(n+1)));
yy_cheb = zeros(size(xx));
for i=1:length(xx)
temp = w./(xx(i)-x_cheb);
yy_cheb(i) = sum(temp.*y_cheb)/sum(temp);
end
figure;
plot(xx, f(xx), 'b-', 'LineWidth', 1.5); hold on;
plot(xx, yy_cheb, 'g-.', 'LineWidth', 1.5);
scatter(x_cheb, y_cheb, 50, 'filled');
legend('真实函数','Chebyshev插值','插值节点');
title('Chebyshev节点插值(11点)');
4. 数值积分的高精度计算
第五章的数值积分习题常要求比较不同求积公式的精度。以习题5.3为例,计算∫₀¹ sin(x²)dx。
4.1 复合求积公式实现
实现复合Simpson公式:
matlab复制function I = composite_simpson(f, a, b, n)
h = (b-a)/n;
x = linspace(a,b,n+1);
y = f(x);
I = h/3 * (y(1) + 4*sum(y(2:2:end-1)) + 2*sum(y(3:2:end-2)) + y(end));
end
测试不同方法的精度:
matlab复制f = @(x) sin(x.^2);
exact = 0.310268301723381; % 精确值参考
methods = {@(f,a,b,n) composite_trapezoid(f,a,b,n), @composite_simpson};
names = {'梯形公式','Simpson公式'};
n_values = [10, 20, 40, 80, 160];
results = zeros(length(methods), length(n_values));
for i = 1:length(methods)
for j = 1:length(n_values)
results(i,j) = methods{i}(f, 0, 1, n_values(j));
end
end
% 显示误差
disp('绝对误差:');
disp(abs(results - exact));
4.2 自适应积分应用
Matlab的integral函数采用自适应算法,我们可以对比:
matlab复制[I, err] = integral(f, 0, 1, 'AbsTol', 1e-10);
disp(['自适应积分结果:', num2str(I)]);
disp(['误差估计:', num2str(err)]);
5. 常微分方程数值解
第九章的ODE问题需要特别注意稳定性。以习题9.4的刚性方程为例:
code复制y' = -1000(y - sin(t)) + cos(t), y(0)=1
5.1 显式与隐式方法对比
实现经典的显式Euler和隐式梯形法:
matlab复制% 显式Euler
function [t, y] = euler_explicit(f, tspan, y0, h)
t = tspan(1):h:tspan(2);
y = zeros(size(t));
y(1) = y0;
for i = 1:length(t)-1
y(i+1) = y(i) + h*f(t(i), y(i));
end
end
% 隐式梯形法
function [t, y] = trapezoidal_implicit(f, tspan, y0, h, tol)
t = tspan(1):h:tspan(2);
y = zeros(size(t));
y(1) = y0;
for i = 1:length(t)-1
% 用不动点迭代解隐式方程
y_guess = y(i);
for iter = 1:100
y_new = y(i) + h/2*(f(t(i),y(i)) + f(t(i+1),y_guess));
if abs(y_new - y_guess) < tol
break;
end
y_guess = y_new;
end
y(i+1) = y_new;
end
end
测试稳定性:
matlab复制f = @(t,y) -1000*(y - sin(t)) + cos(t);
[t1, y1] = euler_explicit(f, [0 1], 1, 0.001); % 步长要很小
[t2, y2] = trapezoidal_implicit(f, [0 1], 1, 0.1, 1e-6);
figure;
plot(t1, y1, 'b-', t2, y2, 'r--');
legend('显式Euler(小步长)','隐式梯形法(大步长)');
title('刚性方程解法比较');
6. 特征值计算的实用技巧
第八章的特征值问题在实际中极为常见。以习题8.2的矩阵为例:
code复制A = [4 -1 1; -1 3 -2; 1 -2 3]
6.1 QR算法实现
虽然Matlab有eig函数,但理解QR算法的实现很有意义:
matlab复制function [V, D] = qr_algorithm(A, max_iter, tol)
V = eye(size(A));
for k = 1:max_iter
[Q, R] = qr(A);
A = R*Q;
V = V*Q;
if max(abs(tril(A,-1))) < tol
break;
end
end
D = diag(diag(A));
end
与内置函数对比:
matlab复制A = [4 -1 1; -1 3 -2; 1 -2 3];
[V1, D1] = qr_algorithm(A, 1000, 1e-6);
[V2, D2] = eig(A);
disp('QR算法结果:'); disp(D1);
disp('Matlab eig结果:'); disp(D2);
6.2 稀疏矩阵处理
对于大型稀疏矩阵,可以用:
matlab复制% 生成一个1000x1000的稀疏矩阵
n = 1000;
A = spdiags([-ones(n,1) 2*ones(n,1) -ones(n,1)], -1:1, n, n);
% 只计算前6个特征值
opts.tol = 1e-6;
opts.maxit = 500;
d = eigs(A, 6, 'largestabs', opts);
disp('前6个特征值:'); disp(d');
