一般而言,在计算 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=3∗38;
3
8
=
3
4
∗
3
4
3^8=3^4*3^4
38=34∗34;
3
4
=
3
2
∗
3
2
3^4=3^2*3^2
34=32∗32;
3
2
=
3
1
∗
3
1
3^2=3^1*3^1
32=31∗31;
3
1
=
3
1
∗
3
0
3^1=3^1*3^0
31=31∗30;
观察这个递归过程,我们可以发现:
当
n
n
n是偶数时
a
n
=
a
n
/
2
∗
a
n
/
2
a^n=a^{n/2}*a^{n/2}
an=an/2∗an/2,;
当
n
n
n是奇数时,
a
n
=
a
∗
a
n
/
2
a^n=a*a^{n/2}
an=a∗an/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=28∗22∗21。
那么现在可以思考三个问题: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
21∗21=22,
2
2
∗
2
2
=
2
4
2^2*2^2=2^4
22∗22=24,
2
4
∗
2
4
=
2
8
2^4*2^4=2^8
24∗24=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=28∗22∗21,需要跳过,在这里我们只需要做个判断即可,利用二进制的位运算很容易:
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,P≤1018
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;
}