返回

算法每日一练:第 61 天——数的幂次快速取模法

前端

引言

在算法竞赛和编程实践中,我们经常会遇到需要计算大整数幂次模的情况。例如,在密码学中,模运算被广泛用于密钥交换和数据加密。传统方法是直接使用循环乘法,时间复杂度为 O(n)。然而,对于大指数 n,这种方法效率低下。

快速幂取模法

快速幂取模法是一种基于二进制思想的快速算法,时间复杂度为 O(logn)。其核心思想是将指数 n 拆分为二进制位,然后使用递归分治的方法逐步计算结果。

具体步骤如下:

  1. 将指数 n 表示为二进制形式:n = (b[k] * 2^k) + (b[k-1] * 2^(k-1)) + ... + b[1] * 2 + b[0]
  2. 递归计算 N^b[k] mod P,结果记为 res
  3. 对于每个 b[i] = 1,将 res 更新为 res * res mod P
  4. 返回 res

代码实现

def fast_pow(N, M, P):
    # 特判 M 为 0 的情况
    if M == 0:
        return 1

    # 将 M 转为二进制表示
    b = []
    while M > 0:
        b.append(M % 2)
        M //= 2

    # 递归计算
    res = 1
    for i in range(len(b) - 1, -1, -1):
        res = (res * res) % P
        if b[i] == 1:
            res = (res * N) % P

    return res

时间复杂度分析

快速幂取模法的递归层数为 O(logn),每次递归需要执行一个乘法和一个取模操作,时间复杂度为 O(1)。因此,总时间复杂度为 O(logn)。

举例

假设我们要计算 2^10000 mod 1000007。

使用快速幂取模法:

b = [0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0]  # 10000 的二进制表示
res = 1
for i in range(len(b) - 1, -1, -1):
    res = (res * res) % 1000007
    if b[i] == 1:
        res = (res * 2) % 1000007

print(res)  # 输出:604661

使用循环乘法:

res = 1
for i in range(10000):
    res = (res * 2) % 1000007

print(res)  # 输出:604661

可以看出,快速幂取模法比循环乘法快得多,尤其当指数 n 较大时。

总结

快速幂取模法是一种高效且实用的算法,可用于快速计算大整数幂次模。该算法基于二进制思想,通过递归分治的方法大幅减少了乘法次数,时间复杂度为 O(logn)。在算法竞赛和实际编程中,快速幂取模法有着广泛的应用。