求和意义和性质
当一个算法包含循环结构, 例如 while 或 for, 我们常常可以把它的运行时间表示为每次迭代所花时间之和. 因此, 求和不仅是数学中的基础工具, 也是算法分析中最常见的表达形式之一.
求和符号
在数学中, 求和符号写作 读作 sigma(西格玛). 它表示一系列数值的累加.1
一个典型写法是:
其中:
- 是起始下标, 表示从第 项开始求和;
- 是终止下标, 表示求到第 项;
- 是被累加的项.
等价地,
如果把上限扩展到无穷大, 就得到无穷级数:2
线性性质
求和最重要的性质之一是线性.
对于任意数列 、, 有:
对于常数 , 有:
合并起来, 可写成:
这一性质非常适合用于拆分复杂求和式, 例如:
常见求和公式
等差和
最经典的求和是自然数和:
用高斯配对法可得:
其增长阶为:
平方和
平方和公式为:
其增长阶为:
立方和
立方和公式为:
其增长阶为:
几何级数
几何级数在算法分析中也极其常见:
当 时, 有:
如果 且 , 则无穷几何级数收敛:
望远镜求和
有些求和式可以通过相邻项抵消, 形成“望远镜”效果.
例如:
展开后大部分项会抵消, 最后只剩首尾项:
这类技巧常用于推导闭式公式, 也常用于证明一些求和恒等式.
与算法分析的关系
很多算法的运行时间都能写成求和形式. 例如:
-
线性扫描:
-
双重循环:
-
嵌套循环但内层次数逐渐变化:
-
分治递归展开后常得到几何级数或混合级数.
因此, 掌握求和不仅是为了做数学题, 更是为了把算法时间复杂度算出来.
编程中的求和
Rust 中可以直接利用迭代器来实现求和.
通用求和
pub fn sigma_sum<T, F>(start: i32, end: i32, f: F) -> T
where
T: std::iter::Sum,
F: Fn(i32) -> T,
{
(start..=end).map(f).sum()
}
// 例: 计算 1^2 + 2^2 + ... + 5^2
let ans: i32 = sigma_sum(1, 5, |x| x * x);
等差和的 O(1) 公式
对于 1 + 2 + ... + n 这类等差和, 可以直接使用公式而不是循环累加:
pub fn gauss_sum(start: i64, end: i64) -> i64 {
let len = end - start + 1;
let sum = start + end;
if len % 2 == 0 {
(len / 2) * sum
} else {
len * (sum / 2)
}
}
这样做把时间复杂度从 \Theta(n) 降到了 \Theta(1).