版权声明:本文为博主原创,未经博主允许不得转载 https://blog.csdn.net/Sherry_Yue/article/details/87885853
快速幂
引题 :现有两个整数m、n,求
m
n
m^n
m n 除以1000000007之后的余数。 输入 :输入整数m、n,用1个空格隔开,占1行。 输出 :输出
m
n
m^n
m n 除以1000000007之后的余数,占1行。 限制 :
1
<
=
m
<
=
100
1<=m<=100
1 < = m < = 1 0 0 ,
1
<
=
n
<
=
1
0
9
1<=n<=10^9
1 < = n < = 1 0 9 输入示例 :5 8 输出实例 :390625
1. 解决算法复杂度问题
如果用最直接的方法求
x
n
x^n
x n ,我们需要进行n-1吃乘法运算,算法复杂度为O(n)。 不过,x的幂乘可以利用
x
n
=
(
x
2
)
n
2
x^n=(x^2)^\frac{n}{2}
x n = ( x 2 ) 2 n 的性质,用反复平方法快速求出。 该算法可以通过下面的递归函数实现:
p
o
w
(
x
,
n
)
=
{
1
(
n
=
0
时
)
p
o
w
(
x
2
,
n
/
2
)
(
n
为
偶
数
时
)
p
o
w
(
x
2
,
n
/
2
)
∗
x
(
n
为
奇
数
时
)
pow(x,n)=\begin{cases}1&(n=0时)\\pow(x^2,n/2)&(n为偶数时)\\pow(x^2,n/2)*x&(n为奇数时)\end{cases}
p o w ( x , n ) = ⎩ ⎪ ⎨ ⎪ ⎧ 1 p o w ( x 2 , n / 2 ) p o w ( x 2 , n / 2 ) ∗ x ( n = 0 时 ) ( n 为 偶 数 时 ) ( n 为 奇 数 时 )
举个例子,
3
21
3^{21}
3 2 1 展开之后如下所示:
3
21
=
(
3
∗
3
)
10
∗
3
=
9
10
∗
3
=
(
9
∗
9
)
5
∗
3
=
8
1
5
∗
3
=
(
81
∗
81
)
2
∗
81
∗
3
=
656
1
2
∗
81
∗
3
=
(
6561
∗
6561
)
1
∗
81
∗
3
3^{21}\\=(3*3)^{10}*3=9^{10}*3\\=(9*9)^5*3=81^5*3\\=(81*81)^2*81*3=6561^2*81*3\\=(6561*6561)^1*81*3
3 2 1 = ( 3 ∗ 3 ) 1 0 ∗ 3 = 9 1 0 ∗ 3 = ( 9 ∗ 9 ) 5 ∗ 3 = 8 1 5 ∗ 3 = ( 8 1 ∗ 8 1 ) 2 ∗ 8 1 ∗ 3 = 6 5 6 1 2 ∗ 8 1 ∗ 3 = ( 6 5 6 1 ∗ 6 5 6 1 ) 1 ∗ 8 1 ∗ 3
这样的话乘法运算的次数就从20次减少到了6次。只要算3 * 3,9 * 9,81 * 81,6561 * 6561以及多出的 *81和 *3,这六次乘法运算就可以。
似乎根据上面的分析我们可以得出代码:
int power ( int a, int b)
{
int ans = 1 ;
while ( b> 0 )
{
if ( b& 1 ) ans = ans* a;
b >>= 1 ;
a = a* a;
}
return ans;
}
这样,递归函数的参数n逐次减半,因此算法复杂度为O(logn)。
2. 解决取模运算问题
在遇到“求某计算结果除以M(本题中式1000000007)之后的余数”这类题时,可以按下述方法计算(这里a除以b之后的余数记作a%b)。
计算加法时,每相加一次执行一次%M
计算减法时,给被减数加上M之后,先算减法,后算%M
计算乘法时,每相乘一次执行一次%M
关于计算乘法时的公式:
(
a
∗
b
)
%
M
=
(
a
%
M
)
∗
(
b
%
M
)
(a*b)\%M=(a\%M)*(b\%M)
( a ∗ b ) % M = ( a % M ) ∗ ( b % M ) 证明: 设a除以M的余数和商分别为ar、aq, b除以M的余数和商分别为br、bq, 即a/M=aq……ar, b/M=bq……br,
则有:
a
∗
b
=
(
a
q
∗
M
+
a
r
)
∗
(
b
q
∗
M
+
b
r
)
=
a
q
∗
b
q
∗
M
2
+
a
r
∗
b
q
∗
M
+
a
q
∗
b
r
∗
M
+
a
r
∗
b
r
=
(
a
q
∗
b
q
∗
M
+
a
r
∗
b
q
+
a
q
∗
b
r
)
∗
M
+
a
r
∗
b
r
a*b\\=(aq*M+ar)*(bq*M+br)\\=aq*bq*M^2+ar*bq*M+aq*br*M+ar*br\\=(aq*bq*M+ar*bq+aq*br)*M+ar*br
a ∗ b = ( a q ∗ M + a r ) ∗ ( b q ∗ M + b r ) = a q ∗ b q ∗ M 2 + a r ∗ b q ∗ M + a q ∗ b r ∗ M + a r ∗ b r = ( a q ∗ b q ∗ M + a r ∗ b q + a q ∗ b r ) ∗ M + a r ∗ b r 即易得:
(
a
∗
b
)
%
M
=
a
r
∗
b
r
=
(
a
%
M
)
∗
(
b
%
M
)
(a*b)\%M=ar*br=(a\%M)*(b\%M)
( a ∗ b ) % M = a r ∗ b r = ( a % M ) ∗ ( b % M )
类似可以得出:
(
a
∗
b
)
%
M
=
[
(
a
%
M
)
∗
(
b
%
M
)
]
%
M
(a*b)\%M=[(a\%M)*(b\%M)]\%M
( a ∗ b ) % M = [ ( a % M ) ∗ ( b % M ) ] % M (引理1:积的取余等于取余的积的取余。)
公式:
a
b
%
M
=
(
a
%
M
)
b
%
M
a^b\%M=(a\%M)^b\%M
a b % M = ( a % M ) b % M 证明:
(
a
%
M
)
b
%
M
=
[
(
a
∗
1
)
%
M
]
b
%
M
=
{
[
(
a
%
M
)
∗
(
1
%
M
)
]
%
M
}
b
%
M
=
[
(
a
%
M
)
%
M
]
b
%
M
(a\%M)^b\%M\\= [(a*1)\%M]^b\%M\\=\{[(a\%M)*(1\%M)]\%M\}^b\%M\\=[(a\%M)\%M]^b\%M
( a % M ) b % M = [ ( a ∗ 1 ) % M ] b % M = { [ ( a % M ) ∗ ( 1 % M ) ] % M } b % M = [ ( a % M ) % M ] b % M 由上面公式迭代:
[
(
a
%
M
)
b
]
%
M
=
a
b
%
M
[(a\%M)^b]\%M=a^b\%M
[ ( a % M ) b ] % M = a b % M
因此,解决了上述两个问题,我们就可以实现快速幂的算法代码了:
#include <iostream>
#define LL long long
using namespace std;
const LL mod= 1e9 + 7 ;
LL ksm ( LL a, LL b)
{
LL ans = 1 ;
a % = mod;
while ( b> 0 )
{
if ( b& 1 ) ans = ( ans* a) % mod;
b >>= 1 ;
a = ( a* a) % mod;
}
return ans;
}