4The Art of Computer Programming, Volume 1

算术运算

数值算法

加法减法乘法除法浮点运算

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第4章:算术运算(Arithmetic Operations)

一、导读

1.1 本章学习目标

第4章深入讲解计算机算术运算的算法实现——加法、减法、乘法、除法和浮点运算。Knuth 从最基础的进位系统开始,逐步构建出高效的算术算法。

本章核心学习目标:

  • 理解不同进位系统:位置记数法、混合进制
  • 掌握高精度加减法:进位传播和借位处理
  • 掌握经典乘法算法:从 O(n²) 的学校乘法到 Karatsuba 算法
  • 理解除法算法:恢复余数法和不恢复余数法
  • 掌握浮点数的算术运算:IEEE 754 标准的实现细节

1.2 为什么本章如此重要

算术运算是所有计算的基础。理解底层算术算法的实现,对于密码学(大数运算)、图形学(浮点精度)、编译器优化(常量折叠)等领域都至关重要。

1.3 核心问题引导

为什么学校教的多位数乘法是 O(n²)?有没有更快的方法?

Karatsuba 算法如何将乘法复杂度降到 O(n¹·⁵⁸⁵)?

浮点加法为什么比整数加法复杂得多?

为什么 0.1 + 0.2 ≠ 0.3?


二、核心概念详解

2.1 位置记数法

b 进制位置记数法:
数 u = (u_{n-1} u_{n-2} ... u₁ u₀)_b
     = u_{n-1} × b^{n-1} + u_{n-2} × b^{n-2} + ... + u₁ × b + u₀

其中 0 ≤ uᵢ < b

常用进制:
- 二进制 (b=2): 计算机内部表示
- 八进制 (b=8): 早期计算机
- 十六进制 (b=16): 现代计算机
- 十进制 (b=10): 人类习惯

2.2 高精度加法

算法 A (高精度加法):
给定 u = (u_{n-1}...u₀)_b 和 v = (v_{n-1}...v₀)_b
求 w = u + v

A1. [初始化] j ← 0, k ← 0
A2. [单字相加] t ← uⱼ + vⱼ + k
     wⱼ ← t mod b
     k ← ⌊t/b⌋
A3. [循环] j ← j + 1
     如果 j < n,返回 A2
A4. [结束] wₙ ← k

时间复杂度: O(n)
空间复杂度: O(n)

2.3 乘法算法

2.3.1 学校乘法(O(n²))

算法 M (多精度乘法):
u = (u_{n-1}...u₀)_b
v = (v_{n-1}...v₀)_b

M1. [初始化] k ← 0
M2. [j=0 的乘法] 计算 u × v₀,结果加到 w 的第 0 到 n 位
M3. [循环] j ← j + 1
     如果 j < n,计算 u × vⱼ,结果加到 w 的第 j 到 j+n 位
     返回 M3

时间复杂度: O(n²) 次单字乘法

2.3.2 Karatsuba 算法(O(n¹·⁵⁸⁵))

Karatsuba 算法 (1960):

将 u 和 v 各分为两半:
u = u₁ × B + u₀  (B = b^{n/2})
v = v₁ × B + v₀

u × v = u₁v₁ × B² + (u₁v₀ + u₀v₁) × B + u₀v₀

关键观察:
u₁v₀ + u₀v₁ = (u₁ + u₀)(v₁ + v₀) - u₁v₁ - u₀v₀

所以只需要 3 次 n/2 位乘法:
1. u₁v₁
2. u₀v₀
3. (u₁ + u₀)(v₁ + v₀)

递推式: T(n) = 3T(n/2) + O(n)
解: T(n) = O(n^{log₂ 3}) = O(n^{1.585})

对比:
n = 1000 位:
- 学校乘法: 1,000,000 次操作
- Karatsuba: ~24,000 次操作
- 加速比: ~42x

2.3.3 更快的乘法

Toom-Cook 算法 (1963):
- 将数字分为 k 段
- 复杂度: O(n^{1+ε}),ε 随 k 增大而减小
- k=3: O(n^{1.465})
- k=5: O(n^{1.376})

Schönhage-Strassen 算法 (1971):
- 基于 FFT
- 复杂度: O(n log n log log n)
- 适用于超大整数(数万位以上)

Harvey-van der Hoeven 算法 (2019):
- 复杂度: O(n log n)
- 理论上最优,但常数极大
- 实际中尚未实用

2.4 浮点运算

IEEE 754 浮点数格式:
(-1)^s × 1.f × 2^{e-bias}

单精度 (32 bit): 1 符号 + 8 指数 + 23 尾数
双精度 (64 bit): 1 符号 + 11 指数 + 52 尾数

浮点加法:
1. 对阶: 将较小指数的数右移,使指数相同
2. 尾数相加
3. 规格化: 调整结果使尾数在 [1, 2) 范围内
4. 舍入: 根据舍入模式处理多余位

精度问题:
0.1 的二进制表示: 0.0001100110011... (无限循环)
0.1 + 0.2 = 0.30000000000000004 (双精度)

解决方案:
- 使用 decimal 类型(金融计算)
- 使用整数运算(以分为单位)
- 使用 epsilon 比较: |a - b| < ε

三、实战案例

3.1 案例一:Karatsuba 乘法实现

python
def karatsuba(x, y):
    if x < 10 or y < 10:
        return x * y
    
    n = max(len(str(x)), len(str(y)))
    half = n // 2
    
    # 分割
    high1, low1 = divmod(x, 10 ** half)
    high2, low2 = divmod(y, 10 ** half)
    
    # 3 次递归乘法
    z0 = karatsuba(low1, low2)
    z2 = karatsuba(high1, high2)
    z1 = karatsuba(low1 + high1, low2 + high2) - z2 - z0
    
    return z2 * 10 ** (2 * half) + z1 * 10 ** half + z0

# 验证
print(karatsuba(123456789, 987654321))
# 输出: 121932631112635269
print(123456789 * 987654321)
# 输出: 121932631112635269

四、本章小结

高精度加法是 O(n),关键在于进位传播

学校乘法是 O(n²),Karatsuba 降到 O(n¹·⁵⁸⁵),FFT 方法可达 O(n log n)

浮点运算需要处理对阶、规格化和舍入,精度问题是固有的


五、思考与练习

思考题

为什么 Karatsuba 算法只需要 3 次乘法而不是 4 次?

浮点数的舍入模式有哪些?各有什么特点?

编程练习

实现高精度乘法的 Karatsuba 算法,并与学校乘法对比性能。

实现 IEEE 754 单精度浮点加法。


4/5算术运算
0%