俄罗斯农名乘法适用于两个正整数相乘,只需要用到乘2、除2和加法操作。公式如下:
该乘法经常被用于两数相乘取模的场景,如果两数相乘已经超过数据范围,但取模后不会超过,我们就可以利用这个方法计算中间结果后取模,保证每次运算都在数据范围内。并且由于乘除2可以用移位来实现,因此非常适合计算机高效实现。
实现:
//递归实现
int ruM(int m, int n)
{
if (m == 1)
return n;
if (m % 2)
return ruM((m - 1) / 2, 2 * n) + n; //在这里进行取模,防止溢出
return ruM(m / 2, 2 * n);
}
//迭代实现
int ruM2(int m, int n)
{
int res = 0;
while (m != 1)
{
if (m % 2)
{
res += n; //在这里取模,防止溢出
m = (m - 1) / 2;
n *= 2;
}
else
{
m /= 2;
n *= 2;
}
}
return res + n;
}
//移位和加法。体现该方法真正的优势:使用移位代替乘除2。
int ruM3(int m, int n)
{
int res = 0;
while (m)
{
if (m & 1)
res += n; //在这里取模,防止溢出
m >>= 1; //移位代替乘除,更高效
n <<= 1;
}
return res;
}
这种拆分求和,防止溢出的思想,可以使用不同的乘除系数,比如可以对第一个乘数乘十,对第二个乘数除十,然后递归计算。当然这种情况下,就不能用移位来代替乘除,因此实现效率不高,下面是示例代码:
int mul(int a, int b)
{
int res = 0;
while (b)
{
if (b % 10)
res += a * (b % 10); //这这里取模,防止溢出
a *= 10; //普通乘除法,效率比移位低
b /= 10;
}
return res;
}