zoukankan      html  css  js  c++  java
  • 1. 快速幂、快速乘

    快速幂

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

    1. 解决算法复杂度问题

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

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

    321=(33)103=9103=(99)53=8153=(8181)2813=65612813=(65616561)18133^{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; //当b为奇数时,乘以余下的一个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

    关于计算乘法时的公式:(ab)%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,

    则有:
    ab=(aqM+ar)(bqM+br)=aqbqM2+arbqM+aqbrM+arbr=(aqbqM+arbq+aqbr)M+arbra*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
    即易得:
    (ab)%M=arbr=(a%M)(b%M)(a*b)\%M=ar*br=(a\%M)*(b\%M)

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

    公式:ab%M=(a%M)b%Ma^b\%M=(a\%M)^b\%M
    证明:
    (a%M)b%M=[(a1)%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=ab%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;
    }
    

    同理,易得快速乘的算法:
    快速乘主要用于防止有两个较大的数相乘而直接乘爆, 因为是加法, 怎么都不可能加爆.,所以目的就是为了防止爆范围。

    #include <iostream>
    #define LL long long
    using namespace std;
    const LL mod=1e9+7;
    
    LL ksc(LL a,LL b)//快速乘,计算a*b%mod
    {
        LL ans = 0;
        a %= mod;
        while( b>0 )
        {
            if( b&1 ) ans = (ans+a)%mod;
            b >>= 1;//位运算,右移1位,相当于除以2
            a = (a+a)%mod;
        }
        return ans;
    }
    
  • 相关阅读:
    Js内存泄漏的几种情况
    简单工厂模式
    单例模式
    设计模式简介
    百度地图api-动态添加覆盖物
    ArcGIS发布地图服务后直接调用查看方法
    NetCDF 共享软件 中文
    Oracle服务器重命名
    NETCDF入门
    Oracle11gExpress和PL/SQL Developer安装
  • 原文地址:https://www.cnblogs.com/yuzilan/p/10626082.html
Copyright © 2011-2022 走看看