美文网首页
俄罗斯农民乘法

俄罗斯农民乘法

作者: 东方胖 | 来源:发表于2022-11-12 12:11 被阅读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

    相关文章

      网友评论

          本文标题:俄罗斯农民乘法

          本文链接:https://www.haomeiwen.com/subject/yfaxxdtx.html