1. 递归专题到了快速幂,这一篇到底想讲透什么
递归专题写到第五篇,我把快速幂放出来,是因为它几乎是这个系列里唯一能同时讲清"递归拆分"和"复杂度优化"的例子。你之前可能已经写过递归处理字符串、递归二路归并排序这类题目,它们更多是让你观察递归一层层展开的顺序。到了快速幂,递归的用途发生了变化:它不是用来遍历结构的,而是用来压缩计算规模的。
不少读者是在 CSP、PTA 或者学校算法作业里碰到快速幂的。题目会计算 a^b mod m,a、b、m 三个数据范围看起来都很正常,但你用一个 for 循环提交,往往会收获一个超时。例如 b 给到 10^9,循环一亿次,在比较严格的判题环境下基本过不去;如果 b 给到 10^18,循环就更不可能了。这个时候最自然的思路,不是找什么奇技淫巧,而是回到指数运算本身的恒等式上。
快速幂有两种常见讲法,一种是按二进制逐位处理,另一种是先算一半,再把一半的结果平方。在递归专题里,我希望你先掌握第二种。原因很简单:第二种的递归式非常清晰,它把 a^n 的规模 n 变成了 n/2 规模的同构子问题,然后做常数时间的合并。这比二进制版本更贴近"递归思想的考试范围"。
换句话说,快速幂就是一个典型的递归式:
- 若 n = 0,a^0 = 1。
- 若 n 是偶数,a^n = (a^(n/2))^2。
- 若 n 是奇数,a^n = (a^(n/2))^2 * a。
递归函数每次只把指数砍一半,所以递归深度大约是 log2(n)。对于 n = 10^18,深度也只有 60 层,完全不用担心 C++ 递归
