快速幂

问题

a b a^b ab, 为了简单起见, 假定 b ≥ 0 且 b 为 整 数 b\ge0 且b为整数 b0b.

朴素求解方法

这个问题很简单, 最简单的想法, a自乘b次即可, 也就是所谓的朴素求解方法:

int pow_naive(int a, int b) {
    int ret = 1;
    
    while (b--) {
        ret *= a;
    }
    return ret;
}

上述方法时间复杂度为 O ( n ) O(n) O(n).

快速幂求解

而"快速幂"方法可以将时间复杂度提升到 O ( log ⁡ n ) O(\log n) O(logn), 它基于这样的想法:
a b a^b ab, 将b展开为二进制表达, 如当b=13时, 有:

b = ( 1101 ) 2 = 1 × 2 0 + 0 × 2 1 + 1 × 2 2 + 1 × 2 3 b = (1101)_2 = 1 \times 2^0 + 0 \times 2^1 + 1 \times 2^2 + 1 \times 2^3 b=(1101)2=1×20+0×21+1×22+1×23
a b = a 2 0 + 0 + 2 2 + 2 3 = a 2 0 × a 2 2 × a 2 3 a^b = a^{2^0 + 0 + 2^2 + 2^3} = a^{2^0} \times a^{2^2} \times a^{2^3} ab=a20+0+22+23=a20×a22×a23

代码如下:

int pow_fast(int a, int b) {
    int ret = 1;
    int base = a;

    while (b) {
        if (b & 1) { // 当前b最低位为1, 比如 1101最右边的1
            ret *= base;
        }
        b >>= 1;
        base *= base;
    }
    return ret;
}

应用示例

可以看一个相关的应用来体会一下用法:
杭电 快速幂模基础板题
题目简单描述为: 1 ≤ N ≤ 1 , 000 , 000 , 000 , 求 N N 的 最 后 一 位 数 1\le N \le 1,000,000,000, 求N^N的最后一位数 1N1,000,000,000,NN.

简单分析下:
显然, 直接算太大, 会整型溢出. 假定结果为M, 最后一位数不就是 M%10嘛.
其实这就是典型的快速幂取模问题.

要使用快速幂取模来解决上述问题, 需要以下公式:
a b m o d    c = ( a m o d    c ) b m o d    c a^b \mod c = (a \mod c)^b \mod c abmodc=(amodc)bmodc

证明如下:

  1. 首先证明:
    a b m o d    c = ( ( a m o d    c ) × ( b m o d    c ) ) m o d    c ab \mod c = ((a \mod c) \times (b \mod c)) \mod c abmodc=((amodc)×(bmodc))modc

    证明过程:
    a m o d    c = r 1 ⇒ a = q 1 c + r 1 b m o d    c = r 2 ⇒ b = q 2 c + r 2 a b m o d    c = ( q 1 c + r 1 ) ( q 2 c + r 2 ) m o d    c = [ q 1 q 2 c 2 + r 1 r 2 + ( q 1 r 2 + q 2 r 1 ) c ] m o d    c = r 1 r 2 m o d    c = ( ( a m o d    c ) × ( b m o d    c ) ) m o d    c a \mod c = r_{1} \Rarr a = q_{1}c + r_{1} \\ b \mod c = r_{2} \Rarr b = q_{2}c + r_{2} \\ \begin{aligned} ab \mod c &= (q_{1}c + r_{1})(q_{2}c + r_{2}) \mod c \\ &= [q_{1}q_{2}c^2 + r_{1}r_{2} + (q_{1}r_{2} + q_{2}r_{1})c] \mod c \\ &= r_{1}r_{2} \mod c \\ &= ((a \mod c) \times (b \mod c)) \mod c \end{aligned} amodc=r1a=q1c+r1bmodc=r2b=q2c+r2abmodc=(q1c+r1)(q2c+r2)modc=[q1q2c2+r1r2+(q1r2+q2r1)c]modc=r1r2modc=((amodc)×(bmodc))modc
    证毕.
    也就是说: 积的余 = 余的积的余

  2. 再证明:
    a b m o d    c = ( a m o d    c ) b m o d    c a^b \mod c = (a \mod c)^b \mod c abmodc=(amodc)bmodc

    证明过程:
    a b m o d    c = ( a × a b − 1 ) m o d    c = [ ( a m o d    c ) × ( a b − 1 m o d    c ) ] m o d    c = [ ( a m o d    c ) × ( a m o d    c ) × ( a b − 2 m o d    c ) ] m o d    c = . . . = ( a m o d    c ) b m o d    c \begin{aligned} a^b \mod c &= (a \times a^{b-1}) \mod c \\ &= [(a \mod c) \times (a^{b-1} \mod c)] \mod c \\ &= [(a \mod c) \times (a \mod c) \times (a^{b - 2} \mod c)] \mod c \\ &= ... \\ &= (a \mod c)^{b} \mod c \end{aligned} abmodc=(a×ab1)modc=[(amodc)×(ab1modc)]modc=[(amodc)×(amodc)×(ab2modc)]modc=...=(amodc)bmodc

有了这个理论, 代码就不难了:

// 快速幂取模 模板代码
int pow_fast_tpl(int a, int b, int c) {
    int ret = 1;
    int base = a % c;

    while (b) {
        if (b & 1) {
            ret = (ret * base) % c;
        }
        base = (base * base) % c;
        b >>= 1;
    }

    return ret;
}

上述代码中, 令a=b=N, c = 10即可求出答案.
上述代码也可以作为"快速幂取模"的模板.

参考

欢迎补充指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值