1. 快速幂

版权声明:本文为博主原创,未经博主允许不得转载 https://blog.csdn.net/Sherry_Yue/article/details/87885853

快速幂

引题:现有两个整数m、n,求 m n m^n 除以1000000007之后的余数。
输入:输入整数m、n,用1个空格隔开,占1行。
输出:输出 m n m^n 除以1000000007之后的余数,占1行。
限制 1 < = m < = 100 1<=m<=100 1 < = n < = 1 0 9 1<=n<=10^9
输入示例:5 8
输出实例:390625

1. 解决算法复杂度问题

如果用最直接的方法求 x n x^n ,我们需要进行n-1吃乘法运算,算法复杂度为O(n)。
不过,x的幂乘可以利用 x n = ( x 2 ) n 2 x^n=(x^2)^\frac{n}{2} 的性质,用反复平方法快速求出。
该算法可以通过下面的递归函数实现:
p o w ( x , n ) = { 1 n = 0 p o w ( x 2 , n / 2 ) n p o w ( x 2 , n / 2 ) x n pow(x,n)=\begin{cases}1&(n=0时)\\pow(x^2,n/2)&(n为偶数时)\\pow(x^2,n/2)*x&(n为奇数时)\end{cases}

举个例子, 3 21 3^{21} 展开之后如下所示:

3 21 = ( 3 3 ) 10 3 = 9 10 3 = ( 9 9 ) 5 3 = 8 1 5 3 = ( 81 81 ) 2 81 3 = 656 1 2 81 3 = ( 6561 6561 ) 1 81 3 3^{21}\\=(3*3)^{10}*3=9^{10}*3\\=(9*9)^5*3=81^5*3\\=(81*81)^2*81*3=6561^2*81*3\\=(6561*6561)^1*81*3

这样的话乘法运算的次数就从20次减少到了6次。只要算3 * 3,9 * 9,81 * 81,6561 * 6561以及多出的 *81和 *3,这六次乘法运算就可以。

似乎根据上面的分析我们可以得出代码:

int power(int a, int b)
{
    int ans = 1;
    while( b>0 ) 
    {
        if( b&1 ) ans = ans*a; //当n为奇数时,乘以余下的一个a
        b >>= 1;//位运算,右移1位,相当于除以2
        a = a*a;
    }
    return ans;
}

这样,递归函数的参数n逐次减半,因此算法复杂度为O(logn)。

2. 解决取模运算问题

在遇到“求某计算结果除以M(本题中式1000000007)之后的余数”这类题时,可以按下述方法计算(这里a除以b之后的余数记作a%b)。

  1. 计算加法时,每相加一次执行一次%M
  2. 计算减法时,给被减数加上M之后,先算减法,后算%M
  3. 计算乘法时,每相乘一次执行一次%M

关于计算乘法时的公式: ( a b ) % M = ( a % M ) ( b % M ) (a*b)\%M=(a\%M)*(b\%M)
证明:
设a除以M的余数和商分别为ar、aq,
b除以M的余数和商分别为br、bq,
即a/M=aq……ar,
b/M=bq……br,

则有:
a b = ( a q M + a r ) ( b q M + b r ) = a q b q M 2 + a r b q M + a q b r M + a r b r = ( a q b q M + a r b q + a q b r ) M + a r b r a*b\\=(aq*M+ar)*(bq*M+br)\\=aq*bq*M^2+ar*bq*M+aq*br*M+ar*br\\=(aq*bq*M+ar*bq+aq*br)*M+ar*br
即易得:
( a b ) % M = a r b r = ( a % M ) ( b % M ) (a*b)\%M=ar*br=(a\%M)*(b\%M)

类似可以得出: ( a b ) % M = [ ( a % M ) ( b % M ) ] % M (a*b)\%M=[(a\%M)*(b\%M)]\%M
(引理1:积的取余等于取余的积的取余。)

公式: a b % M = ( a % M ) b % M a^b\%M=(a\%M)^b\%M
证明:
( a % M ) b % M = [ ( a 1 ) % M ] b % M = { [ ( a % M ) ( 1 % M ) ] % M } b % M = [ ( a % M ) % M ] b % M (a\%M)^b\%M\\= [(a*1)\%M]^b\%M\\=\{[(a\%M)*(1\%M)]\%M\}^b\%M\\=[(a\%M)\%M]^b\%M
由上面公式迭代:
[ ( a % M ) b ] % M = a b % M [(a\%M)^b]\%M=a^b\%M

因此,解决了上述两个问题,我们就可以实现快速幂的算法代码了:

#include <iostream>
#define LL long long
using namespace std;
const LL mod=1e9+7;

LL ksm(LL a,LL b)//快速幂
{
    LL ans = 1;
    a %= mod;
    while( b>0 )
    {
        if( b&1 ) ans = (ans*a)%mod;
        b >>= 1;//位运算,右移1位,相当于除以2
        a = (a*a)%mod;
    }
    return ans;
}

猜你喜欢

转载自blog.csdn.net/Sherry_Yue/article/details/87885853