简介:这份资源是东北大学理学院数学系数值分析课程的实验报告文档,面向正在学习数值分析、需要完成迭代法实验或复习线性方程组求解的学生。报告围绕解线性方程组的迭代法与迭代格式比较展开,完整记录了Jacobi、Gauss-Seidel与SOR三种方法的实验目的、原理、数据记录及结果分析,并附有可直接运行的MATLAB代码,涵盖矩阵分解、迭代控制与误差计算等环节。压缩包内共1个doc文件,约186KB,按学校实验报告规范组织,包含实验项目名称、目的、原理、内容步骤、数据记录处理与结果分析等要素。已有2617人学习下载,读者可据此对照不同初始向量与松弛因子ω下的迭代次数和误差变化,理解收敛速度差异,并借助代码复现实验过程,适合作为课程实验参考与算法实现模板。
1. 从一份实验报告说起:数值分析课设到底要交什么
数值分析这门课,实验报告的“含金量”往往不在结论写得多漂亮,而在你能不能把迭代法的收敛过程讲清楚、把数据记录完整、把代码跑通并解释每一步。很多同学拿到题目第一反应是抄公式、贴代码、凑一张结果表,交上去被追问一句“为什么 SOR 的 ω 取 1.9 迭代次数突然爆炸”就答不上来。这份方案围绕解线性方程组的三种迭代法——Jacobi、Gauss-Seidel、SOR——把实验目的、原理推导、MATLAB 实现、数据记录、结果分析串成一条完整链路,适合正在做数值分析实验报告、课程设计,或者需要一套可复现 MATLAB 代码模板的读者直接参考。下面按“需求怎么立、方案怎么设计、代码怎么落地、结果怎么验收”的顺序展开。
2. 需求与实验目标:三种迭代法要对比出什么
2.1 实验要回答的三个问题
这份实验报告的核心不是“把方程组解出来”,而是通过对比回答三个问题:第一,Jacobi 和 Gauss-Seidel 在同一个方程组上收敛速度差多少,差在哪里;第二,SOR 的松弛因子 ω 如何影响迭代次数,是否存在一个“甜点区”;第三,初始向量从[0;0;0]换成[1;1;1]或[-1;-1;-1],收敛次数会不会明显变化。这三个问题对应实验目的里写的“认识各种迭代法收敛的含义、影响各迭代法收敛速度的因素”,也是答辩时最容易被追问的点。
从交付角度看,一份合格的实验报告需要包含:实验名称、目的与要求、实验原理、实验内容及步骤、实验数据记录与处理、实验结果与分析,并且数据表要能支撑上面的三个问题。很多报告翻车就翻在“只有最终解,没有迭代次数和误差记录”,或者“误差阈值写了 10^-5 但代码里根本没判断”。所以方案设计的第一步,是把“记录什么”定死:每种方法的迭代次数、最终解向量、相邻两次迭代的误差范数,以及 SOR 在不同 ω 下的迭代次数曲线。
2.2 测试方程组与收敛性前提
实验用的方程组是:
4x1 - x2 + x3 = 7 4x1 - 8x2 + x3 = -21 -2x1 + x2 + 5x3 = 15写成矩阵形式Ax = b,其中
A = [ 4, -1, 1; 4, -8, 1; -2, 1, 5] b = [7; -21; 15]精确解是x = [2; 4; 3]。这里有一个容易被忽略的细节:系数矩阵A并不是严格对角占优的,第一行|4| = |-1| + |1|只是弱对角占优,第二行|-8| > |4| + |1|是严格占优,第三行|5| > |-2| + |1|也是严格占优。严格对角占优是 Jacobi 和 Gauss-Seidel 收敛的充分条件,但不是必要条件,这个方程组实际是收敛的,实验数据也验证了这一点。写报告时如果把“对角占优”直接写成“充要条件”,答辩时容易被扣分,通常的做法是写成“充分条件,本实验通过数值结果验证其收敛性”。
2.3 三种方法的迭代格式统一表达
把A拆成A = D - L - U,其中D是对角部分,L是严格下三角部分的相反数,U是严格上三角部分的相反数。三种方法的迭代格式可以统一写成:
- Jacobi:
x^(k+1) = D^(-1)(L+U)x^(k) + D^(-1)b - Gauss-Seidel:
x^(k+1) = (D-L)^(-1)U x^(k) + (D-L)^(-1)b - SOR:
x^(k+1) = (D-ωL)^(-1)[(1-ω)D + ωU]x^(k) + ω(D-ωL)^(-1)b
这个统一表达的好处是,代码里可以复用D、L、U的构造,只换迭代矩阵和右端项,减少重复代码,也方便在报告里用一张表对比三种格式的差别。下面第 3 章就按这个思路做总体设计。
3. 总体方案设计:矩阵拆分、迭代控制与数据记录
3.1 模块划分:四个函数各管一件事
我一般会把整个实验拆成四个模块,而不是把三种方法各写一段大脚本。四个模块分别是:矩阵拆分模块、单步迭代模块、迭代控制模块、数据记录模块。矩阵拆分负责从A生成D、L、U;单步迭代负责根据方法类型算一次x^(k+1);迭代控制负责 while 循环、误差判断和最大迭代次数保护;数据记录负责把每次的迭代次数、误差、解向量存下来,供后面画表和写分析。
这样拆的好处是,SOR 只是单步迭代模块里换一个公式,ω 作为参数传进去,不用复制三份几乎一样的代码。实验报告里贴代码时,也可以先贴公共部分,再贴三种方法的核心一行,篇幅可控,逻辑清楚。
3.2 迭代终止条件与误差度量
实验要求误差阈值取10^-5。这里要明确误差怎么算:是相邻两次迭代解的差的无穷范数,还是残差||b - Ax||的范数。原实验代码用的是dis = abs(X_1 - X_0),然后对每个分量判断dis(i) >= 10^-5,这实际上是逐分量判断,等价于无穷范数小于阈值时才整体停止。写报告时建议统一写成:
||x^(k+1) - x^(k)||_∞ < 10^-5并且加一个最大迭代次数上限,比如 10000 次,防止不收敛时死循环。这个上限在 SOR 的 ω 取到 1.9 时特别重要,实验数据里 ω=1.9 迭代了 11828 次,如果没有上限保护,代码可能跑很久,报告里也不好解释。
3.3 数据记录表的结构设计
数据表建议按“方法 / 参数 / 迭代次数 / 最终解 / 误差”五列组织,SOR 再把 ω 单独列一列。下面是一个表结构示例,实际数值按代码运行结果填:
| 方法 | 参数 | 迭代次数 | x1 | x2 | x3 | 误差 |
|---|---|---|---|---|---|---|
| Jacobi | 初始 [0;0;0] | 14 | 2.0000 | 4.0000 | 3.0000 | <1e-5 |
| Gauss-Seidel | 初始 [0;0;0] | 9 | 2.0000 | 4.0000 | 3.0000 | <1e-5 |
| SOR | ω=1.25 | 15 | 2.0000 | 4.0000 | 3.0000 | <1e-5 |
| SOR | ω=1.50 | 21 | 2.0000 | 4.0000 | 3.0000 | <1e-5 |
| SOR | ω=1.75 | 80 | 2.0000 | 4.0000 | 3.0000 | <1e-5 |
这张表是报告的核心证据,答辩时老师大概率会指着某一行问“为什么这个 ω 迭代次数反而多”。所以表下面必须配一段分析,不能只丢表格。
3.4 初始向量对比的实验设计
原实验分别取了[0;0;0]、[1;1;1]、[-1;-1;-1]三个初始向量。设计上要注意:三个初始向量下,Jacobi 的迭代次数分别是 14、13、14,Gauss-Seidel 都是 9,SOR 在相同 ω 下略有波动。这说明对于这个方程组,初始向量对收敛次数的影响不大,真正影响大的是方法本身和 ω 的取值。报告里可以把这一点写成结论:“初始向量的选择对迭代次数影响有限,收敛速度主要由迭代矩阵的谱半径决定。”这句话是有理论依据的,迭代法的渐近收敛速度由迭代矩阵谱半径决定,初始向量只影响前几步。
4. 核心代码实现:三种迭代法的 MATLAB 落地
4.1 公共矩阵拆分与参数初始化
先写公共部分,把A、b、D、L、U和初始向量准备好。注意 MATLAB 里tril(A,-1)取的是严格下三角,前面加负号才是迭代格式里的L,这个符号很容易写反,写反了迭代就不收敛。
% 公共参数初始化 A = [4, -1, 1; 4, -8, 1; -2, 1, 5]; b = [7; -21; 15]; D = diag(diag(A)); % 对角矩阵 L = -tril(A, -1); % 严格下三角的相反数 U = -triu(A, 1); % 严格上三角的相反数 X0 = [0; 0; 0]; % 初始向量,可换成 [1;1;1] 或 [-1;-1;-1] tol = 1e-5; % 误差阈值 maxIter = 10000; % 最大迭代次数保护这段代码的关键是L和U的符号。如果写成L = tril(A,-1)而不加负号,迭代矩阵就变成D^(-1)(-L+U)之类的错误形式,结果会发散或者收敛到错误的值。血泪经验是:先在纸上把A = D - L - U展开验证一遍,再写代码。
4.2 Jacobi 迭代实现
Jacobi 的特点是每步都用上一轮的X0整体更新,不混用新分量。下面用统一循环结构写,方便和后面两种方法对比:
% Jacobi 迭代 X = X0; iter = 0; err = inf; while err >= tol && iter < maxIter X_new = inv(D) * (L + U) * X + inv(D) * b; % Jacobi 迭代公式 err = norm(X_new - X, inf); % 无穷范数误差 X = X_new; iter = iter + 1; end fprintf('Jacobi: iter=%d, x=[%.4f, %.4f, %.4f], err=%.2e\n', ... iter, X(1), X(2), X(3), err);这里用norm(..., inf)代替逐分量判断,代码更简洁,也和报告里的误差定义一致。inv(D)在D是对角矩阵时可以直接用,但更稳妥的写法是D \ (...),避免显式求逆带来的数值误差。如果报告里要强调“按公式实现”,用inv(D)也可以,但要在分析里说明对角矩阵求逆就是对角元取倒数。
4.3 Gauss-Seidel 迭代实现
Gauss-Seidel 和 Jacobi 的区别在于迭代矩阵从D^(-1)(L+U)换成了(D-L)^(-1)U,右端项也相应变化。代码上只需要改一行:
% Gauss-Seidel 迭代 X = X0; iter = 0; err = inf; while err >= tol && iter < maxIter X_new = inv(D - L) * U * X + inv(D - L) * b; % GS 迭代公式 err = norm(X_new - X, inf); X = X_new; iter = iter + 1; end fprintf('Gauss-Seidel: iter=%d, x=[%.4f, %.4f, %.4f], err=%.2e\n', ... iter, X(1), X(2), X(3), err);D - L是下三角矩阵,求逆的计算量比对角矩阵大,但在这个 3×3 的规模下可以忽略。报告里可以补一句:Gauss-Seidel 每次迭代用到了本轮已经更新的分量,所以通常比 Jacobi 收敛快,实验数据里 9 次对 14 次也印证了这一点。
4.4 SOR 迭代实现与 ω 参数扫描
SOR 在 Gauss-Seidel 基础上引入松弛因子 ω,迭代公式为(D-ωL)^(-1)[(1-ω)D + ωU]。代码里把 ω 作为变量,外面套一层扫描,就能一次跑出不同 ω 的迭代次数:
% SOR 迭代,扫描不同 omega omega_list = [1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90]; for k = omega_list X = X0; iter = 0; err = inf; while err >= tol && iter < maxIter X_new = inv(D - k*L) * ((1-k)*D + k*U) * X ... + k * inv(D - k*L) * b; % SOR 迭代公式 err = norm(X_new - X, inf); X = X_new; iter = iter + 1; end fprintf('SOR omega=%.2f: iter=%d, x=[%.4f, %.4f, %.4f]\n', ... k, iter, X(1), X(2), X(3)); end这段代码跑完会得到一条“ω 增大、迭代次数先缓后陡”的曲线。实验数据里 ω 从 1.1 到 1.5 迭代次数从 11 涨到 21,到 1.8 变成 153,1.9 直接 11828,说明这个方程组的最优 ω 并不在 1.5 以上,而是在 1.1 附近甚至更低。报告里可以据此写:“对于本方程组,ω 超过 1.5 后收敛速度急剧下降,说明松弛因子并非越大越好,实际使用中需要先做小范围扫描确定合理区间。”
4.5 初始向量切换与结果汇总
把X0改成[1;1;1]和[-1;-1;-1]再跑一遍,把结果填进第 3 章的表里。为了避免手工抄错,建议在脚本最后加一段汇总输出,把方法名、ω、迭代次数、最终解拼成矩阵打印出来。这样报告里的数据表和代码输出能对上,答辩时被要求现场跑代码也不慌。
5. 避坑与常见问题:迭代法实验最容易翻车的五个点
5.1 现象:代码跑出来解是NaN或发散
原因通常是L、U的符号写反,或者 SOR 公式里(1-ω)D + ωU的括号漏了。解决方法是先在纸上把A = D - L - U展开,确认D、L、U的每个元素,再对照迭代公式逐项检查。另一个常见原因是inv(D - k*L)里k用了 0 或负数,导致矩阵奇异。
5.2 现象:迭代次数和同学对不上
先确认误差定义是否一致。有人用逐分量abs判断,有人用无穷范数,有人用二范数,阈值虽然都是10^-5,但停止时机不同,迭代次数会差一两次。报告里要写清楚用的是哪种范数,代码和文字保持一致。另外初始向量不同也会差一两次,对比时要把初始向量固定。
5.3 现象:SOR 的 ω=1.9 跑了几万次还没停
这是正常的,实验数据里就是 11828 次。如果没有maxIter保护,代码可能跑到几万次才停,看起来像卡死。解决方法是加最大迭代次数上限,并在报告里说明“ω 过大时迭代矩阵谱半径接近 1,收敛极慢”。这也是一个很好的分析素材,不要删掉。
5.4 现象:报告里的数据表和代码输出对不上
多半是改了代码没重跑,或者手工抄表抄错。习惯做法是让脚本直接输出 Markdown 表格或 CSV,报告里的表从输出复制,不手打。如果时间紧,至少把迭代次数和最终解用fprintf打在同一行,方便核对。
5.5 现象:答辩被问“为什么 Gauss-Seidel 比 Jacobi 快”答不上来
要从迭代矩阵谱半径解释:Gauss-Seidel 的迭代矩阵是(D-L)^(-1)U,Jacobi 是D^(-1)(L+U),对于这个方程组前者谱半径更小,所以收敛更快。报告的分析部分最好把这句话写上,再配实验数据,比只写“因为用了新分量”更有说服力。
6. 验收与答辩:怎么把这份实验报告讲成一份完整方案
验收这份实验报告,我一般按四步走。第一步看结构是否齐全:实验名称、目的、原理、内容步骤、数据记录、结果分析六项缺一不可,缺一项直接扣分。第二步看代码能否复现:把报告里的代码复制到一个新脚本里跑,输出是否和报告数据表一致,这一步能筛掉大部分“抄表没跑代码”的情况。第三步看分析是否有因果:每个数据表下面有没有解释“为什么这个数是这样”,比如 SOR 的 ω 增大迭代次数上升,要能联系到谱半径。第四步看答辩准备:能不能在不看稿的情况下解释三种迭代格式的差别、误差怎么算、初始向量影响大不大。
下面这张验收清单可以直接拿去对照:
| 验收项 | 合格标准 | 常见扣分点 |
|---|---|---|
| 结构完整性 | 六项内容齐全 | 缺实验原理或数据记录 |
| 代码可复现 | 复制即跑,输出与表一致 | 符号写反、缺 maxIter |
| 数据表 | 含迭代次数、解、误差 | 只有最终解 |
| 结果分析 | 每个表有因果解释 | 只贴表不分析 |
| 答辩 | 能解释谱半径与收敛关系 | 只会背公式 |
答辩时有一个具体技巧:主动把 SOR 的 ω 扫描曲线讲成“先降后升”的故事,先说 ω 小的时候迭代次数少,再说 ω 超过某个值后迭代次数陡增,最后落到“松弛因子要扫描确定,不能拍脑袋取大”。这样老师会认为你真的跑过数据、想过原因,而不是背结论。另外,如果被问到“这个方程组是不是对角占优”,不要一口咬定“是”,按第 2 章的分析说“弱对角占优加两行严格占优,数值上收敛”,既准确又显得有准备。
从那以后我每次带这类数值实验,都强制自己先跑一遍最小可复现脚本,把数据表从代码输出直接复制,再动笔写分析。希望这份方案能帮到你,把实验报告从“交差”变成一份能讲清楚的完整交付。
本文还有配套的精品资源,点击获取