【问题标题】:Optimising multiplication modulo a small prime以小素数为模优化乘法
【发布时间】:2012-02-19 00:23:37
【问题描述】:

我需要多次执行以下操作:

  1. 取两个整数a, b
  2. 计算a * b mod p,其中p = 1000000007a, bp 的数量级相同

我的直觉是天真

result = a * b
result %= p

效率低下。我可以优化乘法模 p,就像用 pow(a, b, p) 优化的幂模 p 一样吗?

【问题讨论】:

  • 嗯,一个简单的优化是将所有这些合并到一个语句中......在我的测试中它快了大约 6%。
  • 谷歌搜索“快速模乘”会产生大量论文,例如this one
  • 9 位数字可能太小,对于诸如 Montgomery reduction 之类的特殊算法会产生任何好处。不要过早优化。 a,b(数据结构)的来源是什么?你的分析器说什么?
  • “我有一种感觉……效率低下。”基于什么?你试过了吗?你有什么时间限制?你的整体算法是什么?
  • 如果 p = 1000000007 是固定的,那么似乎可以通过手工编写一点汇编语言来提高速度。当然,这将取决于 CPU 指令集,python 标签阻止我详细说明作为答案。

标签: python math cryptography


【解决方案1】:

如果您用 很多 次阐明了您的意思,则可能会有优化的线索,例如,如果您从高频循环中收集结果,则循环可能会提供优化您日常工作的方法。

假设未优化的循环是:

p = 1000000007
b = 123456789
a = 0
while a < p:
    result = (a * b) % p
    dosomething(a, b, result)
    a += 1

您可以优化高频循环中的 * 和 %:

p = 1000000007
b = 123456789
a = 0
result = (a * b) % p
while a < p:
    dosomething(a, b, result)
    a += 1
    result += b
    if result >= p:
        result -= p

【讨论】:

    【解决方案2】:

    这并不能直接回答问题,但如果您正在寻找性能,我建议您不要在纯 Python 中执行此操作。一些选项:

    • 用 C 语言创建一个小型库来执行计算,并使用 Python 的 ctypes 与其对话。
    • 使用numpy;如果您不想自己处理编译内容,这可能是最好的选择。一次执行一个操作不会比 Python 自己的操作符快,但如果您可以将多个操作符放入一个 numpy 数组中,那么对它们的计算将比 Python 中的等值操作快得多。
    • 使用cython 将变量声明为C 整数;同样,与 numpy 一样,如果您分批执行此操作,您将从中受益最多(因为您还可以优化循环)。

    【讨论】:

    • +1 有一个快速算法,但在 python 中实现它可能不会比 (a*b) % p 快。
    • 您提到了 numpy 和 cython,但是这些库中的哪些函数在一次调用中实现了 (a*b)%p?
    【解决方案3】:

    虽然这非常简单,但您可以尝试一下,并在 mod p 步骤上节省一些时间,方法是根据 1000000007 构建产品列表(列表的大小取决于 a 和 @ 的大小987654324@)。测试每个模数(从最高值开始)。当然,这只有在 a &amp; b &gt;= sqrt(p) * 2 时才有帮助。

    【讨论】:

    • 嘿,这可能是我在我的 p 值中剪切和粘贴额外零的地方!请参阅 BlueRaja-DannyPflughoeft 对我的评论和我的回复。
    • @hardmath 你确实做到了......当时我正在打电话,在公共汽车上。这是颠簸的。数零很难。道歉!
    【解决方案4】:

    要在汇编中进行此计算,但可以从 Python 调用它,我会 从一个尝试inline assembly Python module written in CGCCMSVC 编译器具有内联汇编功能,只是语法不同。

    请注意,我们的模数 p = 1000000007 正好适合 30 位。结果 需要的(a*b)%p 可以在 Intel 80x86 寄存器中计算,给出一些弱 a,b 的限制不能比 p 大很多。

    a,b 的大小限制

    (1)a,b 是 32 位无符号整数

    (2)a*b小于p &lt;&lt; 32,即p乘以2^32

    特别是如果a,b 都小于2*p,将避免溢出。 给定 (1),其中任何一个小于 p 也足够了。

    Intel 80x86 指令 MUL 可以将两个 32 位无符号整数相乘 并将 64 位结果存储在累加器寄存器对 EDX:EAX 中。一些 MUL 的细节和怪癖在本有用的第 10.2.1 节中讨论 summary.

    DIV 指令随后可以将此 64 位结果除以 32 位常数 (模数p),将商存储在 EAX 中,将余数存储在 EDX 中。 请参阅最后一个链接的第 10.2.2 节。我们想要的结果就是那个余数。

    正是这个除法指令 DIV 有溢出的风险,应该 分子 EDX:EAX 中的 64 位乘积给出的商大于 32 位 不满足上述(2)。

    我正在为“概念证明”编写 C/内联汇编中的代码 sn-p。 然而,速度的最大好处将取决于批量处理 要处理的数据a,b,分摊函数调用等的开销 Python(如果那是目标平台)。

    【讨论】:

    • 谢谢。 @BlueRaja-DannyPflughoeft。但是我错误地在我的 p 值中添加了一个额外的零。发布的问题中的版本(1 到 7 之间的 8 个零)只需要 30 位。我检查了一下,在 1 和 7 之间有 9 个零的版本不是素数(可被 23 整除),我会在下次发布我的代码 sn-p 时进行更正。
    【解决方案5】:

    您提到 a, b 与 p 的数量级相同。” 通常在密码学中这意味着 a,b 是接近 p 的大数,但严格小于p.

    如果是这种情况,那么您可以使用简单标识

    把你的计算变成

    result = ((a-p)*(b-p))%p
    

    然后,您将一个大乘法转换为两个大减法和一个小乘法。您必须进行分析以查看哪个更快。

    【讨论】:

    • 如果您可以将所有结果保存在机器本机整数中,而不是要求提升为 Python 的任意精度整数(当值大到足以需要它们时无缝发生),您可以保存很多时间。这看起来是一个很好的方法。 (当然,正如答案所说,您应该自己进行测试,看看它是否实际上更快。)
    • 计时,这需要两倍的时间。
    • 只要 a、b 和 p 都是 32 位整数(如在 OP 中),我认为这不会有帮助。
    猜你喜欢
    • 2016-08-30
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-10
    • 2011-12-16
    • 2010-09-14
    相关资源
    最近更新 更多