快速乘/幂算法
算法思想
在对两个数进行乘法运算的时候,我们一般是直接使用库函数或者是用运算符号“”直接进行计算。但是乘法在计算机中处理的时间并不是这么快的,也要拆分为加法来做的。所以快速乘法会更快地计算ab的结果,如果数字过大就可能会出现溢出的问题,但快速乘法却不会。快速幂也是同样的道理。
例如:5 * 53 = 5 * 110101(二进制表示53)
= 5 * (100000 * 1 + 10000 * 1 + 1000 * 0 + 100 * 1 + 10 * 0 + 1 * 1)
= 5 * (2^5 * 1 + 2^4 * 1 + 2^3 * 0 + 2^2 * 1 + 2^1 * 0 + 2^0 * 1)
= 5 * 2^5 * 1 + 5 * 2^4 * 1 + 5 * 2^3 * 0 + 5 * 2^2 * 1 + 5 * 2^1 * 0 + 5 * 2^0 * 1
从结果可以看出,当a0 = 5的时候,a1 = 5 * 2 * (1或者0,这个取决于第二个因数中的二进制位取1还是0)
其实对于快速幂其实是一样的道理,只不过每一位a更新的时候不是2,而是a=aa。
例如3^5的流程:
3^5
= 3^(1001) (二进制)
= 3^(10001+1000+100+11)
Java代码实现
public long q_mul(long a,long b) {
long result = 0;
while (b != 0) {
if ((b & 1) != 0) {
result = result + b % 2 * a; // b%2获取到第二个因数二进制位数的最后一位
}
a = a * 2; // 后一个计算结果是前一个的2倍,例如:a0 * 2 = a1
b = b / 2; // 第二个因数向右移动一位
}
return result;
}
public long q_pow(long a,long b) {
long result = 0;
while (b != 0) {
if ((b & 1) != 0) {
result = (result + a) % mod;
}
a = a * a;
b = b / 2;
}
return result;
}
适用范围
快速计算a*b % mod的结果(主要目的是换乘法为加法,防止爆数据),或者快速计算a^b % mod 的结果,时间复杂度大大降低。
public long q_mul(long a,long b) {
long result = 0;
while (b != 0) {
if ((b & 1) != 0) {
result = result + b % 2 * a;
}
a = a * 2;
b = b / 2;
}
return result;
}
public long q_mul_mod(long a,long b,long mod) {
long result = 0;
while (b != 0) {
if ((b & 1) != 0) {
result = (result + (b % 2 * a) % mod) % mod;
}
a = (a * 2) % mod;
b = b / 2;
}
return result;
}
public long q_pow_mul_mod(long a,long b,long mod) {
long result = 0;
while (b != 0) {
if ((b & 1) != 0) {
result = (result + a) % mod;
}
a = (a + a) % mod;
b = b / 2;
}
return result;
}
public long q_pow_mod(long a,long b,long mod) {
long result = 1;
while (b != 0) {
if ((b & 1) != 0) {
result = q_pow_mul_mod(result,a,mod);
}
a = q_pow_mul_mod(a,a,mod);
b = b / 2;
}
return result;
}