算法学习笔记(2)快速幂

转自算法学习笔记(4):快速幂 - 知乎 (zhihu.com)

快速幂(Exponentiation by squaring,平方求幂)是一种简单而有效的小算法,它可以以[公式]的时间复杂度计算乘方。快速幂不仅本身非常常见,而且后续很多算法也都会用到快速幂。

递归快速幂

刚刚我们用到的,无非是一个二分的思路。我们很自然地可以得到一个递归方程:

[公式]

计算a的n次方,如果n是偶数(不为0),那么就先计算a的n/2次方,然后平方;如果n是奇数,那么就先计算a的n-1次方,再乘上a;递归出口是a的0次方为1。

递归快速幂的思路非常自然,代码也很简单(直接把递归方程翻译成代码即可):

int qpow(int a, int n)
{
    if (n == 0)
        return 1;
    else if (n % 2 == 1)
        return qpow(a, n - 1) * a;
    else
    {
        int temp = qpow(a, n / 2);
        return temp * temp;
    }
}

在实际问题中,题目常常会要求对一个大素数取模,这是因为计算结果可能会非常巨大,但是在这里考察高精度又没有必要。这时我们的快速幂也应当进行取模,此时应当注意,原则是步步取模,如果MOD较大,还应当开long long。这个还是很常用的。

//递归快速幂(对大素数取模)
#define MOD 1000000007
typedef long long ll;
ll qpow(ll a, ll n)
{
    if (n == 0)
        return 1;
    else if (n % 2 == 1)
        return qpow(a, n - 1) * a % MOD;
    else
    {
        ll temp = qpow(a, n / 2) % MOD;
        return temp * temp % MOD;
    }
}

非递归快速幂

我们换一个角度来引入非递归的快速幂。还是7的10次方,但这次,我们把10写成二进制的形式,也就是 [公式] 。

现在我们要计算 [公式] ,可以怎么做?我们很自然地想到可以把它拆分为 [公式] . 实际上,对于任意的整数,我们都可以把它拆成若干个 [公式] 的形式相乘。而这些[公式],恰好就是 [公式] 、[公式][公式]……我们只需不断把底数平方就可以算出它们。

我们先看代码,再来仔细推敲这个过程:

//非递归快速幂
int qpow(int a, int n){
    int ans = 1;
    while(n){
        if(n&1)        //如果n的当前末位为1
            ans *= a;  //ans乘上当前的a
        a *= a;        //a自乘
        n >>= 1;       //n往右移一位
    }
    return ans;
}

P1226 【模板】快速幂||取余运算 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)这个题可以拿来练手,既有快速幂又有取余运算。

posted @ 2022-04-11 21:08  i天天开心  阅读(72)  评论(0编辑  收藏  举报