2.6 蒙哥马利乘法

蒙哥马利空间乘法

  蒙哥马利算法的发明者不是二战时的英国陆军元帅蒙哥马利。外国嘛,同名同姓的人比较多,而这个蒙哥马利是Peter L. Montgomery,美国数学家,于1985年发明了蒙哥马利算法。
  蒙哥马利算法的重要概念是蒙哥马利空间,该空间的定义是三个数:x、r、n。n是要取模的数,x和r是两个乘数, x ‾ \overline{x} x是x在蒙哥马利空间的表示,按以下公式计算:
x ‾ = x ⋅ r    m o d    n \overline{x}=x\cdot r\;mod\;n x=xrmodn
  蒙哥马利空间的乘法,符号用 ∗ \ast ,比较特殊,按以下规则进行,需要注意的是本文中的 r − 1 r^{-1} r1不是传统数学里的 − 1 -1 1次方,而是模乘逆元
x ‾ ∗ y ‾ = x ⋅ y ‾ = x ⋅ y ⋅ r    m o d    n = x ‾ ⋅ y ‾ ⋅    r − 1    m o d    n = x ⋅ r ⋅ y ⋅ r ⋅    r − 1    m o d    n \overline{x}\ast\overline{y}\\ =\overline{x\cdot y}\\ =x\cdot y\cdot r\;mod\;n\\ =\overline{x}\cdot\overline{y}\cdot\;r^{-1}\;mod\;n\\ =x\cdot r\cdot y\cdot r\cdot\;r^{-1}\;mod\;n xy=xy=xyrmodn=xyr1modn=xryrr1modn
  举个例子:
n = 997 r = 1024 x = 1024 y = 2048 x ‾ : = x ⋅ r    m o d    n = 729 y ‾ : = y ⋅ r    m o d    n = 461 r − 1 = 517 x ‾ ∗ y ‾ : = x ‾ ⋅ y ‾ ⋅    r − 1    m o d    n = 483 n = 997\\ r=1024\\ x=1024\\ y=2048\\ \overline{x}:=x\cdot r\;mod\;n=729\\ \overline{y}:=y\cdot r\;mod\;n=461\\ r^{-1}=517\\ \overline{x}\ast\overline{y}:=\overline{x}\cdot\overline{y}\cdot\;r^{-1}\;mod\;n=483 n=997r=1024x=1024y=2048x:=xrmodn=729y:=yrmodn=461r1=517xy:=xyr1modn=483
  需要注意的是蒙哥马利空间乘法不是蒙哥马利乘法。所以实际上 x ‾ ∗ y ‾ \overline{x}\ast\overline{y} xy x ‾ ⋅ y ‾ \overline{x}\cdot\overline{y} xy的蒙哥马利空间表示。

蒙哥马利约化

  由蒙哥马利空间乘法,我们知道求两个数的乘积取模,或者叫模乘,有两种方法,第一种方法是直接相乘再取模,也就是使用公式 x ⋅ y ⋅ r    m o d    n x\cdot y\cdot r\;mod\;n xyrmodn,第二种方法是使用蒙哥马利空间乘法 x ‾ ⋅ y ‾ ⋅ r − 1    m o d    n \overline{x}\cdot\overline{y}\cdot r^{-1}\;mod\;n xyr1modn
  从表面上看,蒙哥马利空间乘法没有任何优势啊,乘法次数一样多。但是有了蒙哥马利约化后就不一样了。蒙哥马利约化,就是一个由 x x x x ‾ \overline{x} x的算法,也就是由 x ‾ ⋅ y ‾ \overline{x}\cdot\overline{y} xy x ⋅ y ‾ \overline{x\cdot y} xy的算法。
  为了进行蒙哥马利约化Montgomery reduction,我们需要解一个方程。该方程如下:
r ⋅ r − 1 − n ⋅ n ′ = 1 r\cdot r^{-1}-n\cdot n'=1 rr1nn=1
  当然,如果不知道 r − 1 r^{-1} r1时,可以将上述方程视为一个正号变成了负号的线性丢番图方程,可以通过扩展欧几里得法将 r − 1 r^{-1} r1 n ′ n' n求出来。
  有了这个就可以这样计算了:
x ⋅ r − 1 = x ⋅ r ⋅ r − 1 / r = x ⋅ ( 1 − n ⋅ n ′ ) / r ≡ x ⋅ ( 1 − n ⋅ n ′ ) / r + l ⋅ n = ( x − x ⋅ n ⋅ n ′ ) / r + l ⋅ n x\cdot r^{-1}\\ =x\cdot r\cdot r^{-1} /r\\ =x\cdot(1-n\cdot n')/r\\ \equiv x\cdot(1-n\cdot n')/r+ l\cdot n\\ =(x-x\cdot n\cdot n')/r+ l\cdot n xr1=xrr1/r=x(1nn)/rx(1nn)/r+ln=(xxnn)/r+ln
  因为 x ⋅ r − 1 x\cdot r^{-1} xr1同余于 ( x − x ⋅ n ⋅ n ′ ) / r + l ⋅ n (x-x\cdot n\cdot n')/r+ l\cdot n (xxnn)/r+ln,其中 l l l是使得结果为小于n的整数的某个数。我们设置一个变量 q = x ⋅ n ′ q=x\cdot n' q=xn,上面的式子就可以改成:
x ⋅ r − 1 ≡ ( x − q ⋅ n ) / r + l ⋅ n x\cdot r^{-1}\\ \equiv (x-q\cdot n)/r+ l\cdot n xr1(xqn)/r+ln
  又因为整个括号内是要除于r的,所以计算q的时候可以取模r的同余类,于是q可以是 q = ( x    m o d    r ) ⋅ n ′    m o d    r q=(x\;mod\;r)\cdot n'\;mod\;r q=(xmodr)nmodr
  这样就给出了一个由 x x x x ‾ \overline{x} x的算法了,算法如下:
q = ( x    m o d    r ) ⋅ n ′    m o d    r a = ( x − q ⋅ n ) / r w h i l e    a < 0 : a + = n r e t u r n    a q=(x\;mod\;r)\cdot n'\;mod\;r\\ a=(x-q\cdot n)/r\\ while \;a < 0:a+=n\\ return\;a q=(xmodr)nmodra=(xqn)/rwhilea<0:a+=nreturna

模幂乘优化

  蒙哥马利乘法的最大用途是模幂乘优化,也就是解决 a e    m o d    n a^e\;mod\;n aemodn这样的计算问题。首先对于幂运算,我们想到的是进行二进制快速幂算法,减少乘法次数。
  对于 a e    m o d    n a^e\;mod\;n aemodn,算法的步骤是首先计算a的蒙哥马利空间表示 a ‾ \overline{a} a,然后计算 a e ‾ \overline{a^e} ae,然后用蒙哥马利约简计算 a e ‾ ⋅ r − 1 \overline{a^e}\cdot r^{-1} aer1,就完了。不过需要注意的是两点:
  1、必须要满足互质才能解那个丢番图方程;
  2、在二进制快速幂过程中由于每次都乘以了r,所以需要利用蒙哥马利约化乘以 r − 1 r^{-1} r1以减少一个r。
  3、实际应用中的蒙哥马利,远比我说的上述过程复杂,因为用了更多的优化技术,但是我是讲蒙哥马利乘法原理,所以只简单地用了二进制快速幂。

Python实现

def extend_euclid(a: int, b: int):
    if a < b:
        x, y, g = extend_euclid(b, a)
        return y, x, g
    # 保存a/b的系数
    coefficients = []
    while b != 0:
        coefficients.append(a // b)
        a, b = b, a % b
    # 循环代入
    x, y = 1, 0
    coefficients.reverse()
    for i in coefficients:
        x, y = y, x - y * i
    return x, y, a


def diophantine(a, b, c):
    x_g, y_g, g = extend_euclid(a, b)
    if c % g != 0:
        raise 'No solutions'
    return x_g * c // g, y_g * c // g, b // g, -a // g


class Montgomery:

    def __init__(self, r, n):
        self.__r = r
        self.__n = n
        x0, y0, xk, yk = diophantine(r, n, 1)
        self.__r_ = x0
        self.__n_ = y0
        while self.__r_ < 0:
            if xk > 0:
                self.__r_ += xk
                self.__n_ += yk
                print(self.__r_)
            else:
                self.__r_ -= xk
                self.__n_ -= yk

                print(self.__r_, '-')

    def reduce(self, x):
        r, n, n_ = self.__r, self.__n, self.__n_
        q = (x % r) * n_ % r
        a = (x - q * n) // r
        while a < 0:
            a += n
        return a

    def present(self, x):
        return x * self.__r % self.__n

    @staticmethod
    def power(a, e, n):
        r = 2
        while r <= n:
            r <<= 1
        montgomery = Montgomery(r, n)
        # 先使用最笨的办法

        power = 1
        result = montgomery.present(1)
        p = montgomery.present(a)

        while power <= e:
            if power & e != 0:
                result = montgomery.reduce(result * p)
            p = montgomery.reduce(p * p)

            power = power << 1
        return montgomery.reduce(result)

Python测试代码

import unittest

import unittest

import modular_inverse as mi
from montgomery2 import Montgomery


class MyTestCase(unittest.TestCase):
    def test_montgomery_space(self):
        x, y, r, n = 1024, 2048, 1024, 997
        r_ = mi.modular_inverse(r, n)
        x_ = x * r % n
        print(f'r_={r_}')
        print(f"{x}*{r}%{n}={x_}")
        y_ = y * r % n
        print(f"{y}*{r}%{n}={y_}")
        print(f"{x_}*{y_}*{r_}%{n}={x_ * y_ * r_ % n}")

    def test_montgomery_pow(self):
        a, e, n = 3, 777, 17
        r1 = Montgomery.power(a, e, n)
        r2 = a ** e % n
        print(r1 == r2, r1, r2)

    def test_montgomery_reduce(self):
        r, n = 3, 4
        m = Montgomery(r, n)
        x, y = 1, 99
        x_, y_ = x * r % n, y * r % n
        xy_ = x * y * r % n

        print(m.reduce(x_ * y_), xy_)


if __name__ == '__main__':
    unittest.main()

  测试结果:

True 14 14
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醒过来摸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值