快速乘算法
问题
已知正整数a、b、mod,求(a * b) % mod = ?
其中,a、b的数量级很大,接近甚至大于mod的数量级
a * b可能会溢出, 所以不方便直接相乘
解决方案
化乘法为加法,a * b相当于b个a相加
暴力算法
int quik_mul(int a, int b, int mod) {
long long output = 0;
for (int i = 0; i != b; i++)
output = (output + a) % mod;
return output;
}
以上代码时间复杂度为O(b),b的数量级很大,不可行
手算思路
为优化算法,先思考我们如何手算a * b?
例:求10026 * 20202.
解:10026 * 20202
=(10026 * 2)*(20202 / 2) = 20052 * 10101
20202是偶数,提因数2乘10026
=20052 + 20052 * 10100 = 20052 + 40104 * 5050
10101是奇数,先用乘法分配律凑偶,再提因数2乘20052
按照以上奇偶规则,继续计算,同时把凑偶时拆出去的数括起来
=(20052) + 80208 * 2525
=(20052 + 80208) + 80208 * 2524
=(20052 + 80208) + 160416 * 1262
=(20052 + 80208) + 320832 * 631
=(20052 + 80208 + 320832) + 320832 * 630
…
把括号里的数用一个变量单独存储,循环计算直到括号外没有数,此时括号里的和就是答案
代码实现
int quik_mul(int a, int b, int mod) {
long long output = 0; //存储括号里的和的变量
while (b) { //当括号外有数时,b不为0
//当b为偶数时,提取b的因数2乘给a
if (0 == b % 2) {
a = (a*a) % mod;
b = b / 2;
}
//当b为奇数时,提取1个a到括号(output)里,b-1为偶数,再提取b的因数2乘给a
else {
output = (output + a) % mod;
a = (a*a) % mod;
b = (b - 1) / 2;
}
}
return output; //括号里的和就是答案
}
用位运算改进代码
int quik_mul(int a, int b, int mod) {
long long output = 0;
while (b) {
if (b & 1)
output = (output + a) % mod;
a <<= 1;
b >>= 1;
}
return output;
}
快速幂算法
快速幂算法的思路和快速乘算法的思路类似,理解了上面的快速乘算法,再理解快速幂算法就轻而易举了
问题
已知正整数a、b、mod,求(a ^ b) % mod = ?
其中,a、b的数量级很大,接近甚至大于mod的数量级
暴力算法
int quik_power(int a, int b, int mod) {
long long output = 1; //注意初始值是1不是0
for (int i = 0; i != b; i++)
output = (output*a) % mod;
return output;
}
手算思路
例:求3 ^ 18.
解:3 ^ 18
=(3 ^ 2)^(18 / 2)=9 ^ 9
18是偶数,提因数2
=(9) * 9 ^ 8
9是奇数,先用乘法结合律凑偶,再提因数2
=(9) * 81 ^ 4
=(9) * 6561 ^ 2(6561 = 81 * 81)
=(9 * 43046721)
把凑偶时拆出去的数括起来,循环计算直到括号外没有数,此时括号里的积就是答案
代码实现
int quik_power(int a, int b, int mod) {
long long output = 1;
while (b) {
if (0 == b % 2) {
b /= 2;
a = (a*a) % mod;
}
else {
output = (output*a) % mod;
b = (b - 1) / 2;
a = (a*a) % mod;
}
}
return output;
}
用位运算改进代码
int quik_power(int a, int b, int mod) {
long long output = 1;
while (b) {
if (b & 1)
output = (output*a) % mod;
b >>= 1;
a = (a*a) % mod;
}
return output;
}
推广
快速幂算法可以推广到用户定义的数据类型,如矩阵快速幂