数学重学 - 15 求和公式与级数

这是 数学重学路线图 阶段三的子页面

求和公式与级数


为什么要学这个?

你以为”求和”就是小学数学?太天真了

分析嵌套循环复杂度 → 等差求和

理解指数退避重试策略 → 等比求和

估算分布式系统的累计耗时 → 级数

快速排序的期望比较次数 → 调和级数

暴力枚举不同密码长度的总尝试次数 → 等比求和

求和公式是”从具体到抽象”的桥梁,让你一眼看穿循环背后的数学规律


等差数列

定义:每一项与前一项之差恒定(公差 d)

通项公式:$a_n = a_1 + (n-1)d$

求和公式

$S_n = \frac{n(a_1 + a_n)}{2}$

或者:$S_n = n \cdot a_1 + \frac{n(n-1)}{2} \cdot d$

直觉:首尾配对,每对之和相等


高斯求和故事

1 + 2 + 3 + … + 100 = ?

高斯(据说10岁时):首尾配对

1 + 100 = 101

2 + 99 = 101

3 + 98 = 101

…共 50 对

答案 = 50 × 101 = 5050

用公式:S = 100 × (1 + 100) / 2 = 5050 ✓


等差数列常见变体

奇数求和:1 + 3 + 5 + … + (2n-1) = n²

证明:a₁=1, d=2, 共n项,S = n(1 + 2n-1)/2 = n²

偶数求和:2 + 4 + 6 + … + 2n = n(n+1)

连续整数平方和:1² + 2² + … + n² = n(n+1)(2n+1)/6

连续整数立方和:1³ + 2³ + … + n³ = [n(n+1)/2]²


等比数列

定义:每一项与前一项之比恒定(公比 r)

通项公式:$a_n = a_1 \times r^{n-1}$

求和公式(r ≠ 1):$S_n = a_1 \cdot \frac{1 - r^n}{1 - r}$

直觉:等比数列增长极快(r > 1时)或衰减极快(|r| < 1时)


无穷等比级数

当 |r| < 1 时,等比级数收敛:

$S_{\infty} = \frac{a_1}{1 - r}$

例子:1 + 1/2 + 1/4 + 1/8 + … = 1/(1 - 1/2) = 2

直觉:你走到墙壁的一半,再走剩余距离的一半…永远走不到墙,总距离趋近2


等比数列的直觉速算

r = 2 时:1 + 2 + 4 + … + 2^(n-1) = 2^n - 1

这就是”翻倍增长的总和约等于最后一项的两倍”

例:1 + 2 + 4 + 8 + 16 = 31 ≈ 2 × 16


后端应用

嵌套循环复杂度分析

1
2
3
4
5
6
# 这段代码执行了多少次操作?
for i in range(n):
for j in range(i):
do_something()
# j 的次数:0 + 1 + 2 + ... + (n-1) = n(n-1)/2
# 等差求和!所以是 O(n²)

很多 O(n²) 算法的精确操作次数就是 n(n-1)/2

冒泡排序、选择排序的比较次数都是这个


指数退避重试

请求失败后不要立即重试,等待时间指数增长:

第1次重试等 1s,第2次等 2s,第3次等 4s,第4次等 8s…

这是等比数列,公比 r = 2

重试 k 次的总等待时间 = 1 + 2 + 4 + … + 2^(k-1) = 2^k - 1 秒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time
import random

def retry_with_exponential_backoff(func, max_retries=5, base_delay=1.0):
"""指数退避重试策略"""
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if attempt == max_retries - 1:
raise
# 等比数列:delay = base_delay * 2^attempt
delay = base_delay * (2 ** attempt)
# 加入随机抖动,避免所有客户端同时重试(惊群效应)
jitter = random.uniform(0, delay * 0.1)
total_delay = delay + jitter
print(f"第{attempt+1}次重试,等待 {total_delay:.2f}s")
time.sleep(total_delay)

# 总等待时间 = 1 + 2 + 4 + 8 + 16 = 31s(等比求和)

缓存过期时间抖动

如果所有缓存同时过期 → 缓存雪崩

解决:基础过期时间 + 随机抖动

抖动范围通常是基础时间的 10%-20%


大数据应用

分片大小估算

总数据 10TB,分成 n 片处理

每片处理时间与片大小成正比:t(片) ∝ 10TB/n

总时间 = n × (固定开销 + 处理时间) = n × c + 10TB × k / n

这是一个先减后增的函数,存在最优分片数

批量处理的累计耗时

每批处理 1000 条,但随着数据增长,每批变慢

如果第 k 批耗时 = k × t₀(线性增长),总耗时 = t₀ × (1+2+…+n) = t₀ × n(n+1)/2

等差求和告诉你:看起来线性增长的单批耗时,累计起来是二次增长!


安全应用

暴力枚举不同密码长度的总尝试次数

字符集大小 k = 62(大小写+数字)

1位密码:62 种

2位密码:62² 种

n位密码:62^n 种

尝试所有 1 到 n 位的密码:总数 = 62 + 62² + … + 62^n

等比求和:S = 62 × (62^n - 1) / (62 - 1)

关键洞察:总数 ≈ 62^n × (62/61) ≈ 最长密码的尝试次数

即:攻击者几乎所有时间都花在尝试最长的那一位上

暴力破解时间估算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def estimate_brute_force(charset_size, max_length, attempts_per_second):
"""估算暴力破解所有1到max_length位密码的时间"""
total = 0
for length in range(1, max_length + 1):
combinations = charset_size ** length
total += combinations

# 等比求和公式验证
formula_total = charset_size * (charset_size**max_length - 1) / (charset_size - 1)

seconds = total / attempts_per_second
print(f"字符集大小: {charset_size}")
print(f"最大长度: {max_length}")
print(f"总组合数: {total:,.0f}")
print(f"公式验证: {formula_total:,.0f}")
print(f"每秒尝试: {attempts_per_second:,}")

if seconds < 60:
print(f"预计时间: {seconds:.1f} 秒")
elif seconds < 3600:
print(f"预计时间: {seconds/60:.1f} 分钟")
elif seconds < 86400:
print(f"预计时间: {seconds/3600:.1f} 小时")
elif seconds < 365.25*86400:
print(f"预计时间: {seconds/86400:.1f} 天")
else:
print(f"预计时间: {seconds/(365.25*86400):.1f} 年")

# GPU暴力破解 MD5 约 100亿次/秒
estimate_brute_force(62, 8, 10_000_000_000)
print("---")
# bcrypt (cost=12) 约 100次/秒
estimate_brute_force(62, 8, 100)

调和级数

定义:$H_n = 1 + \frac{1}{2} + \frac{1}{3} + … + \frac{1}{n}$

近似:$H_n \approx \ln(n) + \gamma$,其中 $\gamma \approx 0.5772$(欧拉-马斯切罗尼常数)

特点:调和级数发散(趋向无穷),但增长极慢

直觉:增长速度和 ln(n) 差不多,比任何正幂次都慢


和算法分析的关系

快速排序期望比较次数:≈ 2n × Hₙ ≈ 2n ln n ≈ 1.39 n log₂ n

优惠券收集问题:集齐 n 种卡片的期望购买次数 = n × Hₙ ≈ n ln n

例:集齐12生肖邮票期望买 12 × H₁₂ ≈ 12 × 3.1 ≈ 37 张

随机化算法分析中频繁出现


Python 代码:验证求和公式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import math

# ===== 等差数列 =====
def arithmetic_sum_loop(a1, d, n):
"""循环求和"""
return sum(a1 + i * d for i in range(n))

def arithmetic_sum_formula(a1, d, n):
"""公式求和"""
return n * a1 + n * (n - 1) * d // 2

# 验证高斯求和
print("1+2+...+100 =", arithmetic_sum_loop(1, 1, 100))
print("公式结果 =", arithmetic_sum_formula(1, 1, 100))
print("高斯公式 =", 100 * 101 // 2)
print()

# ===== 等比数列 =====
def geometric_sum_loop(a1, r, n):
"""循环求和"""
return sum(a1 * r**i for i in range(n))

def geometric_sum_formula(a1, r, n):
"""公式求和"""
if r == 1:
return a1 * n
return a1 * (1 - r**n) / (1 - r)

# 验证:1 + 2 + 4 + 8 + ... + 2^9
print("1+2+4+...+512 =", geometric_sum_loop(1, 2, 10))
print("公式结果 =", geometric_sum_formula(1, 2, 10))
print("2^10 - 1 =", 2**10 - 1)
print()

# ===== 无穷等比级数 =====
def infinite_geometric_partial(a1, r, n_terms):
"""前n项和,观察收敛"""
partial = 0
for i in range(n_terms):
partial += a1 * r**i
return partial

print("无穷等比级数 a1=1, r=0.5 收敛过程:")
for n in [5, 10, 20, 50]:
s = infinite_geometric_partial(1, 0.5, n)
print(f" 前{n:>2}项和 = {s:.10f} (理论极限 = 2.0)")
print()

# ===== 调和级数 =====
def harmonic_number(n):
return sum(1/k for k in range(1, n+1))

print("调和级数 vs ln(n) + γ:")
gamma = 0.5772156649
for n in [10, 100, 1000, 10000, 100000]:
hn = harmonic_number(n)
approx = math.log(n) + gamma
print(f" H({n:>6}) = {hn:.6f}, ln({n})+γ = {approx:.6f}, 误差 = {abs(hn-approx):.6f}")

Python 代码:模拟指数退避

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import random

def simulate_exponential_backoff(success_prob=0.3, max_retries=8, base_delay=1.0):
"""模拟指数退避重试"""
total_wait = 0
for attempt in range(max_retries):
if random.random() < success_prob:
print(f"第{attempt+1}次尝试成功!总等待时间: {total_wait:.2f}s")
return True, total_wait

delay = base_delay * (2 ** attempt) # 等比数列
jitter = random.uniform(0, delay * 0.1)
wait = delay + jitter
total_wait += wait
print(f"第{attempt+1}次失败,等待 {wait:.2f}s (累计 {total_wait:.2f}s)")

print(f"全部{max_retries}次重试失败,总等待: {total_wait:.2f}s")
return False, total_wait

# 运行模拟
random.seed(42)
simulate_exponential_backoff()

print("\n--- 理论最大等待时间 ---")
for max_r in range(1, 9):
total = sum(2**i for i in range(max_r)) # 等比求和
print(f"最多重试{max_r}次: 最大等待 = {total}s = {total/60:.1f}min")

Python 代码:级数收敛/发散可视化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import matplotlib.pyplot as plt
import numpy as np

n_terms = 50
x = np.arange(1, n_terms + 1)

# 等比级数 (|r| < 1) - 收敛
geometric_partial = np.cumsum(0.5 ** np.arange(n_terms))

# 调和级数 - 发散(但很慢)
harmonic_partial = np.cumsum(1.0 / x)

# p=2 级数 - 收敛
p2_partial = np.cumsum(1.0 / x**2)

fig, axes = plt.subplots(1, 3, figsize=(18, 5))

axes[0].plot(x, geometric_partial, 'b-', linewidth=2)
axes[0].axhline(y=2.0, color='r', linestyle='--', label='极限 = 2')
axes[0].set_title('等比级数 (r=0.5) - 收敛到 2')
axes[0].set_xlabel('项数')
axes[0].set_ylabel('部分和')
axes[0].legend()
axes[0].grid(True, alpha=0.3)

axes[1].plot(x, harmonic_partial, 'g-', linewidth=2)
axes[1].plot(x, np.log(x) + 0.5772, 'r--', linewidth=1, label='ln(n) + γ')
axes[1].set_title('调和级数 - 发散(慢慢趋向无穷)')
axes[1].set_xlabel('项数')
axes[1].set_ylabel('部分和')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

axes[2].plot(x, p2_partial, 'm-', linewidth=2)
axes[2].axhline(y=np.pi**2/6, color='r', linestyle='--', label=f'极限 = π²/6 ≈ {np.pi**2/6:.4f}')
axes[2].set_title('p=2 级数 - 收敛到 π²/6')
axes[2].set_xlabel('项数')
axes[2].set_ylabel('部分和')
axes[2].legend()
axes[2].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('series_convergence.png', dpi=150)
plt.show()

常见误区

误区1:等比求和只在数学题里出现

指数退避、分治递归的时间分析、缓存层级延迟,全是等比

误区2:调和级数收敛

不!调和级数发散,只是发散得极慢

但 p > 1 的级数(如 1/n²)收敛

误区3:n(n-1)/2 和 n² 差很多

n(n-1)/2 = n²/2 - n/2,大O意义下就是 O(n²)

但精确计算时差一倍,实际工程中这个常数可能很重要

误区4:无穷级数的和等于”加了无穷多次”

数学上是极限的概念,不是真的加了无穷次

发散级数的”和”没有意义(如 1+2+3+…=”=-1/12” 是解析延拓,不是普通求和)


练习题

练习1:一个API使用指数退避策略,基础等待1s,公比2,最多重试10次。如果全部失败,总等待时间是多少?

答案:S = 1 + 2 + 4 + … + 2^9 = 2^10 - 1 = 1023 秒 ≈ 17 分钟

练习2:分析这段代码的精确执行次数和大O复杂度

1
2
3
4
count = 0
for i in range(n):
for j in range(i, n):
count += 1

答案:j 的循环次数分别是 n, n-1, n-2, …, 1。总次数 = n + (n-1) + … + 1 = n(n+1)/2。大O = O(n²)。

练习3:某垃圾邮件发送者每天发送量翻倍(第1天发1000封,第2天2000封…)。7天内总共发了多少封?如果要追踪”至少发了100万封”的时间点呢?

答案:7天总量 = 1000 × (2^7 - 1) = 1000 × 127 = 127,000 封。累计达100万:1000 × (2^n - 1) ≥ 1,000,000 → 2^n ≥ 1001 → n ≥ 10(第10天累计约102.3万封)。

练习4:优惠券收集问题——系统有20种不同的随机奖励,期望收集多少次才能集齐?

答案:期望次数 = 20 × H₂₀ = 20 × (1 + 1/2 + 1/3 + … + 1/20) ≈ 20 × 3.5977 ≈ 71.95 次。

练习5:证明 1 + 1/2 + 1/4 + 1/8 + … = 2

答案:这是首项 a₁=1、公比 r=1/2 的无穷等比级数。|r|=1/2 < 1,收敛。S = a₁/(1-r) = 1/(1-1/2) = 1/(1/2) = 2。


上一章 目录 下一章
14-函数增长与大O 数学重学路线图 16-概率论基础