【问题标题】:Large Integer Arithmetic - how to implement modulo?大整数算术 - 如何实现模?
【发布时间】:2015-01-19 09:30:00
【问题描述】:

我想实现自己的(简单)大/任意整数精度算术,首先在 Java 中(因为我更熟悉语法),然后将其重写为 C。

我有无限长度数字的加法、减法和乘法,现在我需要模数来进行加密应用。

我将任意数字的数字存储在一个数组中,我按照以下指南了解如何存储数字: How to handle very large numbers in Java without using java.math.BigInteger

所以例如我要计算

849465603662254214335539562 % 578907659710377778063722

当我有两个数组时:

int[] a = [8, 4, 9, 4, 6, 5, 6, 0, 3, 6, 6, 2, 2, 5, 4, 2, 1, 4, 3, 3, 5, 5, 3, 9, 5, 6, 2]
int[] b = [5, 7, 8, 9, 0, 7, 6, 5, 9, 7, 1, 0, 3, 7, 7, 7, 7, 8, 0, 6, 3, 7, 2, 2]

代表这些数字。

什么是尽可能简单的解决方案

int[] c = modFunction(a, b)

感谢任何帮助。

【问题讨论】:

标签: java math arbitrary-precision integer-arithmetic


【解决方案1】:

在计算D mod M 时,您可以从D 中减去M 的任意整数倍而不改变结果。如果您减去商D/M 的近似值,您将更接近所需的模数。重复直到商0会给你答案。

while D >= M
  Q= some integer approximation of D / M
  D= D - Q.M

要获得这样的商近似值,请取 DM 的最高有效数字 K 并计算 Q=10^K.D/M 的整数部分。这可以使用双精度算术方便地完成,并为您提供K 数字(您最多可以使用K=15)。在减法之前添加len(D)-len(M)-K 零以重新对齐。

请注意,在将 DM 的近似值相除(除以第一个 K 数字)时,在 K 数字之后截断可能会导致商出现小错误。 (我的猜测是Q 上的最大误差是一个单位。)这个误差并不重要,因为只要减去M 的整数倍,D 仍然是一个精确值。只有最后你需要检查0<=D<M

在给定的示例中,849465603662254214335539562 mod 578907659710377778063722,近似商为10^15.849465603662254 / 578907659710377 = 1467359412876373.,您需要添加-12 零(!)进行重新对齐,即将小数点左移并使用1467

那么849465603662254214335539562 - 1467 * 578907659710377778063722 = 208066867130013916059388就是请求的模数。

【讨论】:

    【解决方案2】:

    方法一

    我想出了这个方法;它不一定有效,但确实有效。

    请注意,您可以使用输入的长度(以数字为单位)来计算其对数
    您可以使用它来执行除法,从而执行模数。

    具体来说,首先要注意

    849465603662254214335539562 / (578907659710377778063722 * 1000) = 1.4...
    

    因此

    849465603662254214335539562 - 578907659710377778063722 * 1000 = 270557943951876436271817562
    

    现在请注意

    270557943951876436271817562 / (578907659710377778063722 * 100) = 4.6...
    

    因此

    270557943951876436271817562 - (578907659710377778063722 * 400) = 38994880067725325046328762
    

    现在请注意

    38994880067725325046328762 / (578907659710377778063722 * 10) = 6.7...
    

    因此

    38994880067725325046328762 - (578907659710377778063722 * 60) = 4260420485102658362505442
    

    最后,请注意

    4260420485102658362505442 / (578907659710377778063722 * 1) = 7.3...
    

    因此

    4260420485102658362505442 - (578907659710377778063722 * 7) = 208066867130013916059388
    

    所以答案是208066867130013916059388

    仅通过检查长度即可轻松获得 10 的幂,您可以通过乘法尝试所有 10 种可能性并找出给出非负数的最高值来找出需要减去的倍数结果。

    方法二

    只需使用乘法对商进行二进制搜索!然后用商求余数。

    【讨论】:

    • 第一种方法看起来很有趣!我会尝试实现它。
    • @YvesDaoust:他应该如何直接求商?这基本上是在乞求问题。
    • 没错,对不起。我正在解除我苛刻的 cmets。
    • @YvesDaoust:双精度仅适用于最多约 308 位数字,当您谈论 bignums 时,这并不是很多。此外,它完全没有抓住问题的重点。
    • 我将其标记为答案,因为我尝试实现它并且它似乎工作正常,非常简单且足够快,足以满足我的需求。如果有人需要超快速和优化的解决方案,请不要实现它或使用真正的库。 :)
    【解决方案3】:

    取模非常简单:

    a % b = a - floor((a / b)) * b
    

    您只需要整数除法(或 floor() 和除法)、乘法、减法。我想你已经有这些操作了。

    如果你只有整数,则不需要floor() 函数:

    a % b = a - (a / b) * b
    

    例子:

    849465603662254214335539562 % 578907659710377778063722 =
    849465603662254214335539562 - (849465603662254214335539562  / 578907659710377778063722) *  578907659710377778063722 =
    849465603662254214335539562 - 1467 * 578907659710377778063722 =
    849465603662254214335539562 - 849257536795124200419480174 =
    208066867130013916059388
    

    【讨论】:

    • 我还没有按我说的划分,但如果需要我会考虑实现它。
    • 除法和模数就像双胞胎。如果你有一个,很快你就会需要另一个。而且我认为实现一个简单的划分更容易。
    【解决方案4】:

    为什么不想使用 BigDecimal 类?它有 remainder 方法,它完全符合您的要求。您可以查看source code of BigDecimal class 来查看它是如何实现的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-27
      • 2011-02-28
      • 1970-01-01
      • 2015-10-05
      • 2010-09-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多