- 快速幂(在lgn时间计算
x
y
x^y
xy
步骤,将y转化为二进制,举个具体例子,假设y=13 = 1101(2),有
x 13 = x 1101 = x 2 3 ⋅ 1 ⋅ x 2 2 ⋅ 1 ⋅ x 2 1 ⋅ 0 ⋅ x 2 0 ⋅ 1 x^{13} = x^{1101} = x^{{2^3}\cdot1}\cdot x^{{2^2} \cdot1} \cdot x^{{2^1}\cdot0} \cdot x^{{2^0}\cdot1} x13=x1101=x23⋅1⋅x22⋅1⋅x21⋅0⋅x20⋅1
可以看出,我们从低位到高位,依次取出y的每一位进行累计乘,将每次将基x = x * x, 得到如下代码
long long Pow(long long x, long long y){
long long res = 1;
while(y){
//累乘
if(y & 1)
res = fast(res, x);
//叠加
x = fast(x, x);
//减少乘次数
y = y >> 1;
}
return res;
}
其中fast函数是一个大数乘法并对结果取模
//乘法
inline long long fast(long long x, long long y){
return ((x % mod) * (y % mod)) % mod;
}
2.大数乘积取模
利用:
(
x
×
y
)
%
m
o
d
=
(
(
x
%
m
o
d
)
×
(
y
%
m
o
d
)
)
%
m
o
d
(x\times y) \% mod =((x \% mod) \times (y\%mod)) \% mod
(x×y)%mod=((x%mod)×(y%mod))%mod
但这种情况只实用与mod在INT表示的范围类型内的数字,如果mod很大,
(
(
x
%
m
o
d
)
×
(
y
%
m
o
d
)
)
((x \% mod) \times (y\%mod))
((x%mod)×(y%mod)) 相乘还是有可能溢出,这种情况,就只能将乘法转换成加法进行操作
long long fast(long long x, long long y){
long long res = 0;
x %= mod;
y %= mod;
while(y){
if(y & 1){
//加法代替乘法,防止越界
res += x;
if(res >= mod)
res -= mod;
}
y = y >> 1;
x = x << 1;
if(x >= mod)
x -= mod;
}
return res;
}
这段代码是一种快速乘法的算法,它的原理是利用二进制的位运算来将乘法转换为加法,从而避免溢出。
具体来说,假设我们要计算ab,我们可以将b转换为二进制表示,比如b=11,那么b的二进制是1011,那么我们可以将ab写成:
a ∗ b = a ∗ ( 2 3 ) ∗ 1 + a ∗ ( 2 2 ) ∗ 1 + a ∗ ( 2 1 ) ∗ 1 + a ∗ ( 2 0 ) ∗ 0 a*b = a*(2^3)*1 + a*(2^2)*1 + a*(2^1)*1 + a*(2^0)*0 a∗b=a∗(23)∗1+a∗(22)∗1+a∗(21)∗1+a∗(20)∗0
= ( a < < 3 ) + ( a < < 2 ) + ( a < < 1 ) + 0 = (a<<3) + (a<<2) + (a<<1) + 0 =(a<<3)+(a<<2)+(a<<1)+0
其中,<<表示左移运算符,即将一个数的二进制位向左移动一定的位数,相当于乘以2的相应次方。例如,a<<3就相当于a*8。
那么,我们可以用一个循环来实现这个过程,每次判断b的最低位是否为1,如果是,就将a累加到结果中;然后将a左移一位,将b右移一位(即除以2),直到b变为0为止。这样就完成了快速乘法的算法。
代码中的mod表示取模运算,即对结果进行求余数,这是为了防止结果过大而溢出。所以每次累加或者左移之后都要对mod取余。
代码中的&表示按位与运算符,即将两个数的二进制位进行逐位的与运算,只有当两个位都为1时才得到1,否则得到0。例如,11&1=1,因为11的二进制是1011,而1的二进制是0001,只有最低位都为1时才得到1。所以代码中用b&1来判断b的最低位是否为1。