幂运算
幂运算是一种幂的运算,它有一个性质:同底数幂相乘,底数不变,指数相加
求幂运算的朴素做法
O
(
k
)
O(k)
O(k)
假设此时我们需要求底为a ,指数为k 的幂的值,即求
r
e
s
=
a
k
res = a^k
res=ak,那么我们很容易想到最直接的做法:令
r
e
s
=
1
res = 1
res=1,让res 乘以k 次a 即可得到答案。
代码:
int power(int a,int k){
int res = 1;
while(k --) res *= a;
return res;
}
容易看出,它总共进行了k次运算,因此时间复杂度为
O
(
k
)
O(k)
O(k)
快速幂
O
(
l
o
g
k
)
O(logk)
O(logk)
有时候当要求的次方大于一定值时,用朴素做法求幂就会显得效率很低,那么有没有什么方法可以快速求幂呢? 这就需要用到上方的性质:同底数幂相乘,底数不变,指数相加 假设我们需要求
3
7
3^7
37,那么我们可以分为
3
?
3
?
3
?
3
?
3
?
3
?
3
3 * 3 * 3 * 3 * 3 * 3 * 3
3?3?3?3?3?3?3,也可以等于
3
2
?
3
2
?
3
2
?
3
3^2 * 3^2 * 3^2 * 3
32?32?32?3,原因是它们的指数之和为
7
7
7,那么我们就会想到二进制优化
7
=
4
+
2
+
1
=
2
2
+
2
1
+
2
0
=
11
1
2
7 = 4 + 2 + 1 = 2^2 + 2^1 + 2^0 = 111_2
7=4+2+1=22+21+20=1112? 于是
3
7
=
3
11
1
2
3^7 = 3^{111_2}
37=31112? 因此我们只需要处理出指数k的二进制表示及每个二进制数下的乘数即可。
- 如果
k 为0 ,停止运算 - 每当
k
k
k的最低位数为
0 时,
r
e
s
res
res不进行运算,为1时,
r
e
s
=
r
e
s
?
a
res = res * a
res=res?a - 然后将
a
=
a
?
a
a = a * a
a=a?a(每个二进制数下的乘数)
- 最后
k
>
>
=
1
(
k
=
k
/
2
)
k >>= 1(k = k / 2)
k>>=1(k=k/2)
求
3
7
(
3
11
1
2
)
3^7(3^{111_2})
37(31112?):
r
e
s
=
1
res = 1
res=1
k
!
=
0
k != 0
k!=0 k的最低位为1,
r
e
s
=
r
e
s
?
a
=
1
?
3
=
3
res = res * a = 1 * 3 = 3
res=res?a=1?3=3;
a
=
a
?
a
=
9
a = a * a = 9
a=a?a=9
k
>
>
=
1
k >>= 1
k>>=1
k
=
k
/
2
=
3
(
11
1
2
>
>
1
=
=
1
1
2
)
k = k / 2 = 3(111_2 >> 1 == 11_2)
k=k/2=3(1112?>>1==112?)
k
!
=
0
k != 0
k!=0 k的最低位为1,
r
e
s
=
r
e
s
?
a
=
3
?
9
=
27
res = res * a = 3 * 9 = 27
res=res?a=3?9=27;
a
=
a
?
a
=
81
a = a * a = 81
a=a?a=81
k
>
>
=
1
k >>= 1
k>>=1
(
k
=
k
/
2
=
1
,
(
1
1
2
>
>
1
=
=
1
2
)
)
(k = k / 2 = 1,(11_2 >> 1 == 1_2))
(k=k/2=1,(112?>>1==12?))
k
!
=
0
k != 0
k!=0 k的最低位为1,
r
e
s
=
r
e
s
?
a
=
27
?
81
=
2187
res = res * a = 27 * 81 = 2187
res=res?a=27?81=2187;
a
=
a
?
a
=
6561
a = a * a = 6561
a=a?a=6561
k
>
>
=
1
k >>= 1
k>>=1
(
k
=
k
/
2
=
0
,
(
1
2
>
>
1
=
=
0
2
)
)
(k = k / 2 = 0,(1_2 >> 1 == 0_2))
(k=k/2=0,(12?>>1==02?))
k
=
=
0
k == 0
k==0 返回
r
e
s
=
2187
res = 2187
res=2187
代码
int qmi(int a,int k){
int res = 1;
while(k){
if(k & 1) res = res * a;
a = a * a;
k >>= 1;
}
return res;
}
含求模运算的代码
int qmi(int a,int k,int p){
int res = 1;
while(k){
if(k & 1) res = (long long)res * a % p;
a = (long long)a * a % p;
k >>= 1;
}
return res;
}
可以看出它只需要进行
l
o
g
k
logk
logk次运算,因此时间复杂度为
l
o
g
k
logk
logk
|