快速幂(参考OI Wiki)
背景
计算𝑎的𝑛次方表示将𝑛个𝑎乘在一起:𝑎 𝑛 = 𝑎 × 𝑎 ⋯ × 𝑎 ( 𝑛 个 a ) 𝑎^𝑛 =𝑎×𝑎⋯×𝑎(𝑛 个a) a n = a × a ⋯ × a ( n 个 a ) 。然而当 𝑛 太大或单次乘法开销太大的时侯,这种方法就不太适用了
基本思想
将取幂的任务按照指数的 二进制表示 来分割成更小的任务。
具体实现
迭代版本
设 𝑛 的二进制表示为 ( 𝑛 𝑡 𝑛 𝑡 − 1 ⋯ 𝑛 1 𝑛 0 ) 2 (𝑛_𝑡𝑛_{𝑡−1}⋯𝑛_1𝑛_0)_2 ( n t n t − 1 ⋯ n 1 n 0 ) 2 ,也就是说,有
𝑛 = 𝑛 𝑡 2 𝑡 + 𝑛 𝑡 − 1 2 𝑡 − 1 + ⋯ + 𝑛 1 2 1 + 𝑛 0 2 0 𝑛=𝑛_𝑡2^𝑡+𝑛_{𝑡−1}2^{𝑡−1}+⋯+𝑛_12^1+𝑛_02^0
n = n t 2 t + n t − 1 2 t − 1 + ⋯ + n 1 2 1 + n 0 2 0
其中,𝑛𝑖 ∈{0,1} 。那么,就有
𝑎 𝑛 = 𝑎 𝑛 𝑡 2 𝑡 + 𝑛 𝑡 − 1 2 𝑡 − 1 + ⋯ + 𝑛 1 2 1 + 𝑛 0 2 0 𝑎^𝑛=𝑎^{𝑛_𝑡2^𝑡+𝑛_{𝑡−1}2^{𝑡−1}+⋯+𝑛_12^1+𝑛_02^0}
a n = a n t 2 t + n t − 1 2 t − 1 + ⋯ + n 1 2 1 + n 0 2 0
注意,只有 𝑛 𝑖 = 1 𝑛_𝑖 =1 n i = 1 的项才会真正出现在乘积的计算中。
根据这一表达式,可以首先在 O ( l o g 𝑛 ) O(log𝑛) O ( l o g n ) 时间内计算出 𝑎的 O ( l o g 𝑛 ) O(log𝑛) O ( l o g n ) 个 2 𝑘 2^𝑘 2 k 次幂的取值,然后花费 O ( l o g 𝑛 ) O(log𝑛) O ( l o g n ) 的时间选择等于 1 的二进制位对应的幂次乘到最终结果中。这就是快速幂的迭代版本实现。
递归版本
这一过程同样可以通过递归形式实现。注意到,指数 𝑛 的二进制展开可以递归地写作
( 𝑛 𝑡 𝑛 𝑡 − 1 ⋯ 𝑛 1 𝑛 0 ) 2 = 2 × ( 𝑛 𝑡 𝑛 𝑡 − 1 ⋯ 𝑛 1 ) 2 + 𝑛 0 (𝑛_𝑡𝑛_{𝑡−1}⋯𝑛_1𝑛_0)_2=2×(𝑛_𝑡𝑛_{𝑡−1}⋯𝑛_1)_2+𝑛0 ( n t n t − 1 ⋯ n 1 n 0 ) 2 = 2 × ( n t n t − 1 ⋯ n 1 ) 2 + n 0
因此,幂次 𝑎 𝑛 𝑎^𝑛 a n 可以递归地计算为
1 2 3 4 aⁿ = ⎧ 1, 当 n = 0 ⎨ (a^(n/2))², 当 n 为偶数 ⎩ a × (a^((n-1)/2))², 当 n 为奇数
这就是快速幂的递归版本实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def fast_power_mod (a, n, m ): """ 计算 a^n mod m 的递归实现 参数: a: 底数 n: 指数 m: 模数 返回: a^n mod m 的结果 """ if n == 0 : return 1 half = fast_power_mod(a, n // 2 , m) if n % 2 == 0 : return (half * half) % m else : return (a * half * half) % m print (fast_power_mod(2 , 10 , 1000 ))