快速幂,矩阵快速幂(c++ 描述)

今天想介绍一个在中低难度算法竞赛中经常碰到的算法---快速幂,于此相匹配的还有一个叫矩阵快速幂的东西。不过这篇文章只介绍快速幂,之后有时间再更新矩阵快速幂的知识。

快速幂,顾名思义,就是快速求解某一个数的指数次方结果为多少。

普通求解

如让你求2^5次方,你可以很快计算出结果为2*2*2*2*2=32。

这种方式在计算机中可以使用pow函数解决,即pow(2,5)=32。引入头文件cmath就行了,当然你也可以手写。

pow函数的实现方式大致如下

typedef long long ll;
ll pow(ll a,ll b,ll c)
{
    ll d=1;
    while(b--){
        d=d*a%c;
    }
    return d;
}

不难看出,这种计算方式的速度是由b决定的,即时间复杂度为O(b),而如果b很大的,那就超时了啊!

 


快速幂

下面就进入本节的主要内容,用快速幂求解,这其实是对二进制的巧妙使用。

讲一个例子,现在要求3^11,按上述方法需要计算11次,但快速幂可以将求3^11简化为(3^8)*(3^2)*(3^1),这样就只计算了3次,其实总共比较了4次,往下看就懂了。

来一波分析:

  1. 将指数11转化为二进制,即00...001011(32位,前面的0我就省略不写了,太多容易看乱)
  2. 从右到左开始,32位的01串我们在下面给它们一个数值标识,从3^1开始,以平方的方式不断递增,如下面的表格所示(注意只给出最后边的6位,int类型总共应该有32位) 
001011
3^323^163^83^43^23^1

 其实下面的指数也可以有另外一种方式理解,即当前位置的1表示十进制的哪个数,指数就是哪个。

所以下面的操作就很简单了,3^11=3^(8+2+1),为什么要拆成这几个数呢,因为他们就是11转成二进制后,对应的1的位置数值的表示啊(上面的表格),而这些位置的1代表的数加起来就是11. 

那么计算的思路就是,读取11的二进制数,若该位置为1,就乘上该位置所代表的数,若为0则不作处理。

时间复杂度从O(N)降到了O(logN)

具体方法:将11与1做按位与位运算,若结果为1,说明11二进制最后的数为1,所以乘上。

然后下一步我们让11做右移位运算,即将32个01集体往右移动1位,这时最左侧会补上0,而最右侧的1已经不见了。相当于继续比较下一位,若当前数值为0,即二进制32个都为0,说明已经没有1了,就可以结束循环。

具体的代码实现 

#include <iostream>
using namespace std;
typedef long long ll;
/*ll pow(ll a,ll b,ll c)
{
    ll d=1;
    while(b--){
        d=d*a%c;
    }
    return d;
}*/
ll quickPow(ll a,ll b,ll c)
{
    ll d=1;
    while(b){
        if(b&1){
            d=d*a%c;
        }
        a=a*a%c;//更新当前位置代表的数
        b=b>>1;//右移1位
    }
    return d;
}
int main()
{
    cout<<"10的1亿次方mod3的结果为: "<<quickPow(10,1e9,3);
    return 0;
}

矩阵快速幂

typedef long long ll;
const int mod=1e9+7;
const int MaxN=105;//矩阵大小
struct Mat{
    ll m[MaxN][MaxN];
}ans,a;//结果与输入矩阵
Mat Mul(Mat a,Mat b,int n){
    Mat c;
    memset(c.m,0,sizeof(c.m));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                c.m[i][j]=(c.m[i][j]+(a.m[i][k]*b.m[k][j])%mod)%mod;
    return c;
}
Mat _power(Mat a,ll b,int n){
    for(int i=1;i<=n;i++)
        ans.m[i][i]=1;
    while(b){
        if(b&1)
            ans=Mul(ans,a,n);
        a=Mul(a,a,n);
        b>>=1;
    }
    return ans;
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值