科学仿真 · 从零学起 第 1 / 7 节

第 1–2 章 · 数值品味的底线

这本是一本"配方手册",但第 1-2 章讲的是配方背后的品味:什么样的数值错误会毁掉计算,什么样的习惯能提前发现它们。这两章短,却是全书质量的地基。

误差的三种来源与对应的坏味道

  • 舍入误差:浮点只有约 16 位十进制精度,大量相减相近数(灾难性抵消)会把有效位吃光。坏味道:公式里出现 $a-b$ 且 $a\approx b$——改写公式(如用 $x-y = \frac{x^2-y^2}{x+y}$ 类恒等变换)。
  • 截断误差:用有限阶近似替代无限过程(如用短级数代替无穷级数)。坏味道:加密网格/加项后结果仍在漂移。
  • 病态放大:问题本身对输入敏感。坏味道:微小参数扰动引起巨大输出变化——这时要改问题(正则化、重新参数化),不是改算法。

浮点的必修常识

  • 判断相等永远用 $\|x-y\| \le \epsilon\max(\|x\|,\|y\|)$,不用 "=="。
  • 求和精度:小数在前、成对求和(pairwise)或 Kahan 补偿求和;$10^8$ 个数朴素求和能丢掉一半有效位。
  • 下溢、上溢的处理:中间量用对数域或缩放(如贝塞尔函数、阶乘的计算)。

"好程序"的数值清单

书中列的规矩至今有效:参数检查(负数开方、零除)、迭代法必配最大步数与收敛容差、避免隐式假设(假设矩阵正定、假设函数凸)。所有配方函数都带错误退出路径——这是它区别于课堂代码的地方。

原文要点:第 1 章给出舍入误差的标准模型并强调"算法稳定性优先于速度";第 2 章以"解线性方程的 LU 分解与迭代精化"作为第一个完整配方示例,示范书中配方的三要素——算法说明、实现、使用注意事项(limitations 栏目)。

老师的提醒

  • 把"收敛了吗"打印出来成为习惯:任何迭代配方不报告残差就调用,等于盲飞。
  • 灾难性抵消的快速体检:看结果对输入第 8 位有效数字的敏感度,反应剧烈就要重写公式。

自检

  • 三种误差各自的典型症状与对策?
  • Kahan 求和解决什么问题?代价是什么?
  • 为什么说"问题的病态要改问题、算法的不稳要改算法"?

更深一层:数值品味的本质是"误差预算"思维

第 1-2 章教的其实是一种会计学:任何计算都有一个误差预算,工程师的职责是知道预算花在哪了。一次典型的科学计算有四本账:模型误差(物理假设的近似)、离散误差(网格/步长截断)、舍入误差(浮点表示)、统计误差(如果用了随机方法)。新手把所有误差混为一谈,熟手先问"哪一本账最大"——因为优化小账是浪费:离散误差 $10^{-3}$ 主导时,把舍入从 $10^{-15}$ 压到 $10^{-16}$ 毫无意义。这个"木桶效应"思维把数值方法的无限细节压缩成一个可执行的流程:每做一个计算,先估四本账的量级,再决定优化谁。

舍入误差还有个隐蔽的放大器值得单独认识:条件放大。$fl(a+b)$ 本身只错 $\mathbf{u}$ 量级,但如果 $a + b$ 之后还要做 $(a+b) - c$ 且 $a + b \approx c$(灾难性抵消),结果的相对误差就被放大成 $\mathbf{u} \cdot |a+b|/|结果|$——中间量很大、结果很小时,16 位精度瞬间蒸发。识别这类结构的直觉("两个大数相减求小量")是数值品味的核心,解药是恒等变形(如二次方程求根用 $q = -\frac12(b + \mathrm{sign}(b)\sqrt{b^2-4ac})$ 的稳定形式)。

推导细读:Kahan 补偿求和为什么能"作弊"

朴素求和的误差 $\le (n-1)\mathbf{u}S$ 来自每步把小加数"丢进"大和里时的舍入。Kahan 的算法用一个补偿变量 $c$ 把每步被丢掉的低位记账:

$$y = x_i - c,\quad t = S + y,\quad c = (t - S) - y,\quad S = t$$

其中 $c = (t - S) - y$ 这一步在数学上恒为零,但浮点下它恰好等于"这一步丢失的量"——下一步用 $x_i - c$ 把它找补回来。误差从 $O(n\mathbf{u}S)$ 降到 $O(\mathbf{u}S)$,与 $n$ 无关。代价:每步 4 次浮点操作(约 4 倍耗时)。工程变体:成对求和(误差 $O(\mathbf{u}S\log n)$、可向量化)、numpy 的 pairwise 默认、并行归约的树形结构——同一个问题的四种"账本管理策略"。

在真实世界里:一次真实的事故复盘

数值品味的经典教学案例:某弹道仿真中,导弹落点误差随飞行时间发散,团队排查传感与模型无果,最终定位到状态更新里两个相近大数相减(位置更新用了 $x_{new} = x + v\Delta t$,而后续修正项基于 $x_{new}$ 与参考弹道之差)——长飞行时间下参考弹道与实际弹道都巨大、差值微小,16 位精度被吃光。修复:改用偏差量积分(积分相对参考弹道的差分而非绝对位置)。教训的通用形式:长时间积分系统,状态变量应选"偏离参考的小量"而非绝对量——航天器轨道力学、量子化学的 SCF、气候模式的通量守恒约束,全是这一条的应用。

常见误区与辨析

  • "double 就够了":对条件数温和的问题够;条件放大或超长求和链会让 16 位不够——误差预算思维帮你判断"够不够",而不是教条。
  • "收敛了就对了":迭代法收敛到的是离散方程的解;离散误差独立存在,两条账不能互相抵赖。
  • "数值错误总会显现为崩溃":多数数值错误是静默的——不崩溃、结果看着合理、偏差藏在小数点后;只有误差预算习惯能暴露它们。

专题深入:误差预算的"四本账"实务

误差预算思维值得做成可执行的清单:模型误差(物理假设的近似——湍流模型、材料模型)、离散误差(网格/步长截断——可估可控)、舍入误差(浮点表示——$\mathbf{u}$ 级)、统计误差(随机方法——$N^{-1/2}$ 级)。每做一个计算,先估四本账的量级,再决定优化谁——木桶效应:优化小账是浪费(离散误差 $10^{-3}$ 主导时压舍入无意义)。这个思维在工程报告里的形态:误差来源的分解表——负责任的数值报告应该能说出每一层的量级。

灾难性抵消的识别直觉("两个大数相减求小量")是数值品味的核心:$x - y$ 且 $x \approx y$ 时相对误差被放大 $\frac{|x|+|y|}{|x-y|}$ 倍。解药是恒等变形(二次方程求根的稳定形式、双曲函数的差分形式)——看到相近数相减就改写公式。

数学细节展开:Kahan 求和的"记账"机制

Kahan 补偿求和的机制值得展开:补偿变量 $c$ 把每步被丢掉的低位记账——$c = (t - S) - y$ 这一步在数学上恒为零,但浮点下它恰好等于"这一步丢失的量",下一步用 $x_i - c$ 找补回来。误差从 $O(n\mathbf{u}S)$ 降到 $O(\mathbf{u}S)$——与 $n$ 无关。代价:每步 4 次浮点操作(约 4 倍耗时)。成对求和(误差 $O(\mathbf{u}S\log n)$、可向量化)与 Kahan(误差与 $n$ 无关、不可向量化)的权衡是现代并行求和的设计空间。

工程案例细节:数值品味的"代码清单"

NR 前两章的规矩值得做成代码审查清单:参数检查(负数开方、零除、数组越界)、迭代法必配最大步数与收敛容差(不收敛要有明确报错而非静默)、避免隐式假设(假设矩阵正定、假设函数凸——假设要在文档里写清)、所有配方函数都带错误退出路径。"好程序"的数值清单是"坏结果"的防火墙——每一条都对应一类真实事故。

常见误区与辨析(补充)

  • "double 就够了":对条件数温和的问题够;条件放大或超长求和链会让 16 位不够——误差预算思维帮你判断"够不够"。
  • "收敛了就对了":迭代法收敛到的是离散方程的解;离散误差独立存在,两条账不能互相抵赖。

例题

计算 $\sum_{i=1}^{10^8} 1$(双精度)。朴素顺序求和的最坏误差是多少?成对求和呢?

解答

朴素求和:部分和最终为 $10^8$,每步舍入 $\le \mathbf{u}\cdot S_i$,最坏总误差

$$(n-1)\,\mathbf{u}\,S \approx 10^8\times1.1\times10^{-16}\times10^8 = 1.1\times10^{0} \approx 1$$

——错到个位数(相对误差 $10^{-8}$,约 8 位有效数字没了 8 位中的一大半误差积累在尾部大和数上)。

成对求和:误差 $\approx \log_2 n\cdot\mathbf{u}\cdot S = 27\times1.1\times10^{-16}\times10^8 \approx 3\times10^{-7}$——好 6 个数量级。

Kahan 补偿:同样 $\sim O(\mathbf{u}S)$ 与 $n$ 无关。工程结论:大数组求和永远不要朴素顺序累加。

本页是《数值方法(Numerical Recipes)》第 1 节的学习笔记:为便于初学,讲解、例题与要点概括均为本站原创整理,公式与章节定位供对照原书使用。

📑 本书目录 经典研读