俄罗斯农民乘法

2022-11-12  本文已影响0人  东方胖

也称为快速乘法

将被乘数除以2,乘数乘以2,如果被乘数除以2 有余数,则将乘数累加到结果中。

我们使用一个例子来逐步展开来看看这个过程。
计算 78 × 57

第一步,将 78 确定为被乘数,57 确定为乘数,这个顺序可以随意;
累计结果是 sum, a = 78, b = 57, 列表表示计算过程:

a b 余数 sum
78 57 0 0
39 114 1 114
19 228 1 342
9 456 1 798
4 912 0 798
2 1824 0 798
1 3648 1 4446

实际上,上面的乘法原理可以表示成
78 = 64 + 8 + 4 + 2 = 2^6 + 2^3 + 2^2 + 2^1
78 × 57 = (2^6 + 2^3 + 2^2 + 2^1) * 57 = 57 << 6 + 57 << 3 + 57 << 2 + 57 << 1

任何正整数可以分解成这样:
a = 2^{i_1} + 2^{i_2} + ... + 2^{i_k}
于是 a × b 可以表示这样:
b( 2^{i_1} + 2^{i_2} + ... + 2^{i_k})
对每个 2^{i_k}b 项,相当于将 b 左移 i_k 位。
因此俄罗斯农民算法的本质是二进制拆分,然后将有 1 的 位进行累计
上面的表的除2取余的过程,实际上就是二进制拆分的过程。

写成 C++ 程序

int multiply(int a, int b) {
    int s = 0;
    while (a >= 1) {
         if (a % 2) {
            s += b;
            // s %= M 如果要取模在这里增加模运算
        } 
        a >>= 1;
        b <<= 1;
    }
    return s;
}

这个算法由于使用了移位运算,速度非常快。应用场景用于移位取模,两数相乘非常大,超过数据类型的表示范围,但是取模之后还在范围内,那么可以用这个算法对累计和每次取模,可以快速计算出一个大数对某个数的模运算的结果。

不要用这个算法代替乘法运算符 例如用C++ 重载的特性取代,没有什么性能优化,也容易产生数据溢出和截断的 bug

上一篇下一篇

猜你喜欢

热点阅读