快速幂算法

一般而言,在计算 a n a^n an时,我们会使用暴力的方法,逐个去乘,这样操作时间复杂度是 O ( N ) O(N) O(N)

而是用快速幂算法时间复杂度可以降到 O ( l o g N ) O(logN) O(logN)

递归思想

在计算时 ​ a n ​a^n an,我们可以先计算 a 2 a^2 a2,然后在计算 ( a 2 ) 2 (a^2)^2 (a2)2,一直计算到 n n n次幂。

例如 3 9 3^9 39,可以这样递归:
3 9 = 3 ∗ 3 8 3^9=3*3^8 39=338;
3 8 = 3 4 ∗ 3 4 3^8=3^4*3^4 38=3434;
3 4 = 3 2 ∗ 3 2 3^4=3^2*3^2 34=3232;
3 2 = 3 1 ∗ 3 1 3^2=3^1*3^1 32=3131;
3 1 = 3 1 ∗ 3 0 3^1=3^1*3^0 31=3130;
观察这个递归过程,我们可以发现:
n n n是偶数时 a n = a n / 2 ∗ a n / 2 a^n=a^{n/2}*a^{n/2} an=an/2an/2,;
n n n是奇数时, a n = a ∗ a n / 2 a^n=a*a^{n/2} an=aan/2;
代码如下:

long long fastPow(long long a, long long n){
	if(n == 1) return a;
	long long ans = fastPow(a,n/2); 
	if(n % 2 == 1) return ans * ans * a; 
	else return ans * ans;
}

位运算

它是以数的二进制为基础,利用二进制的特性进行计算;

例如:计算 2 11 2^{11} 211,我们可以把它分成 2 8 2^8 28 ​ ​ 2 2 ​​2^2 22 2 1 2^1 21的乘积,即 ​ ​ 2 11 = 2 8 ∗ 2 2 ∗ 2 1 2^{11}=2^8*2^2*2^1 211=282221

那么现在可以思考三个问题:1、如何求 2 8 2^8 28 ​ ​ 2 2 ​​2^2 22 2 1 2^1 21的值,2、如何把 n n n分成 11 = 8 + 2 + 1 11=8+2+1 11=8+2+1,3、如何跳过不需要的部分。

1、如何求 2 8 2^8 28 ​ ​ 2 2 ​​2^2 22 2 1 2^1 21的值
我们可以很容易发现 2 1 ∗ 2 1 = 2 2 2^1*2^1=2^2 2121=22 2 2 ∗ 2 2 = 2 4 2^2*2^2=2^4 2222=24 2 4 ∗ 2 4 = 2 8 2^4*2^4=2^8 2424=28等等,都是 2 2 2的倍数,并且都是倍乘关系,通过递推就可以实现。
2、如何把n分成 11 = 8 + 2 + 1 11=8+2+1 11=8+2+1
用二进制我们便可以轻松理解,把 n n n转换为二进制数,二进制数中的每一位的权值都是低一位的两倍,对应的是倍乘关系,例如 1 1 10 = 101 1 2 = 2 3 + 2 1 + 2 0 = 8 + 2 + 1 11_{10}=1011_2=2^3+2^1+2^0=8+2+1 1110=10112=23+21+20=8+2+1
3、如何跳过不需要的部分
例如 2 11 2^{11} 211, 2 11 = 2 8 ∗ 2 2 ∗ 2 1 2^{11}=2^8*2^2*2^1 211=282221,需要跳过,在这里我们只需要做个判断即可,利用二进制的位运算很容易:
a、 n & 1 n\&1 n&1,去 n n n的最后一位,并且判断这一位是否需要跳过。
b、 n > > = 1 n>>=1 n>>=1,把 n n n右移一位,去掉 n n n的最后一位。

long long fastPow(long long a, long long n){
	long long ans = 1;
	while(n){
		if(n&1) ans *= a;
		a = a* a;
		n >>= 1;
	} 
	return ans;
}

现在我们在思考一个问题当 n n n或者 a a a比较大时,我们的结果往往都会非常大,如果直接计算的话就会发生溢出,所以我们在计算的过程中要对结果进行取模运算。

typedef long long ll;
ll qmul(ll n, ll a, ll mod){
	ll res = 1;
	while(n){
		if(n & 1) res = ((res % mod) * (a % mod)) % mod;
		n >>= 1;
		a = ((a % mod) * (a % mod)) % mod;
	}
	return res % mod;
}

快速幂+快速乘: A , B , P ≤ 1 0 18 A,B,P\leq10^{18} A,B,P1018

ll mul_mod(ll a,ll b,ll mod){
    ll ret = 0;
    while(b){
        if(b & 1) ret = (ret + a) % mod;
        a = (a + a) % mod;
        b >>= 1;
    }
    return ret;
}
ll pow_mod(ll a,ll b,ll mod){
    ll ret = 1;
    while(b){
        if(b & 1) ret = mul_mod(ret, a, mod);
        a = mul_mod(a, a, mod);
        b >>= 1;
    }
    return ret;
}

大数快速幂: n > l o n g   l o n g n>long\:long n>longlong

#include <bits/stdc++.h>

using namespace std;
const int mod=1e9+7;
long long quick_mod(long long a,long long b)
{
    long long ans=1;
    while(b){
        if(b&1){
            ans=(ans*a)%mod;
            b--;
        }
        b/=2;
        a=a*a%mod;
    }
    return ans;
}//内部也用快速幂
long long quickmod(long long a,char *b,int len)
{
    long long ans=1;
    while(len>0){
        if(b[len-1]!='0'){
            int s=b[len-1]-'0';
            ans=ans*quick_mod(a,s)%mod;
        }
        a=quick_mod(a,10)%mod;
        len--;
    }
    return ans;
}

int main(){
    char s[100050];
    int a;
    while(~scanf("%d",&a))         //求a^s%mod
    {
        scanf("%s",s);
        int len=strlen(s);
        printf("%I64d\n",quickmod(a,s,len));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星*湖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值