关于快速乘
这里说的快速乘并不是计算两数的乘法, 而是计算 a * b % p
主要利用公式:
(
a
×
b
)
m
o
d
  
c
=
(
a
m
o
d
  
c
∗
b
m
o
d
  
c
)
m
o
d
  
c
(a×b) \mod c=(a \mod c * b \mod c) \mod c
(a×b)modc=(amodc∗bmodc)modc
(
a
+
b
)
m
o
d
  
c
=
(
a
m
o
d
  
c
+
b
m
o
d
  
c
)
m
o
d
  
c
(a+b) \mod c=(a \mod c+ b \mod c) \mod c
(a+b)modc=(amodc+bmodc)modc
计算a * b % p
时, 若直接计算a * b
可能会溢出, 所以会把乘法转为加法
每一步都进行mod
运算, 这样会减小乘法计算中溢出的几率
将乘法转换为加法:
123 ∗ 567 = 123 ∗ 7 ∗ 1 0 0 + 123 ∗ 6 ∗ 1 0 1 + 123 ∗ 5 ∗ 1 0 2 123*567 = 123*7*10^0+123*6*10^1+123*5*10^2 123∗567=123∗7∗100+123∗6∗101+123∗5∗102
对于二进制来说同理:
6
∗
5
(
二
进
制
101
)
=
6
∗
1
∗
2
0
+
6
∗
0
∗
2
1
+
6
∗
1
∗
2
2
6*5(二进制101) = 6*1*2^0+6*0*2^1+6*1*2^2
6∗5(二进制101)=6∗1∗20+6∗0∗21+6∗1∗22
所以会得到快速乘的计算方法, 求幂同理
#include <stdio.h>
// 快速乘: a * b % p
int mul_mod(int a, int b, int p)
{
int r=0;
while(b)
{
if(b&1) r=(r+a)%p;
a=a*2%p;
b>>=1;
}
return r;
}
// 快速幂: a ^ b % p
int pow_mod(int a, int b, int p)
{
int r=1;
while(b)
{
if(b&1) r=r*a%p;
a=a*a%p;
b=b>>1;
}
return r;
}
// 另一种计算快速幂方法, 与快速乘结合
int pow_mod2(int a, int b, int p)
{
int r=1;
while(b)
{
if(b&1) r=mul_mod(r, a, p);
a=mul_mod(a, a, p);
b=b>>1;
}
return r;
}
int main()
{
// 12345678 * 12345678 会发生溢出
printf("int类型大小: %d\n", sizeof(int));
printf("快速乘: %d\n", mul_mod(12345678, 12345678, 1234));
printf("普通乘: %d\n", 12345678*12345678%1234); // 溢出
printf("快速幂: %d\n", pow_mod(12345678, 12345678, 1234)); // 溢出
printf("方法二: %d\n", pow_mod2(12345678, 12345678, 1234));
return 0;
}
总结
上面的计算结果为:
int类型大小: 4
快速乘: 200
普通乘: 1144
快速幂: 1118
方法二: 1204
实际上 快速乘 还有 快速幂 这样的说法有一定误导性, 将乘法转换为加法, 幂转换为乘法并不一定能提高计算速度, 主要还是为了溢出的考虑, 更像是对大数的运算.