【问题标题】:Replacing BigInteger with UInt64 for Prime factorization algorithms where N < 2^63对于 N < 2^63 的素数分解算法,用 UInt64 替换 BigInteger
【发布时间】:2015-08-13 11:18:52
【问题描述】:

我有一个使用 Pollard's Rho and Brent's algorithms 在 VB.Net 中使用 BigInteger 实现素数分解的好解决方案(请参阅:https://stackoverflow.com/a/31978350/44080

对于N&lt; 2^63,我相信UInt64 应该足够大,并且可能(很多?)更快。

但是我的UInt64 转换在此行失败:

y = ((y^2) Mod n + c) Mod n 'fails when y^2 > UInt64.Max

将此行改为

y = CULng((CDbl(y^2) Mod n + c) Mod n)

由于类型转换处于循环中,因此会降低性能。

请问我该如何解决这个问题?

如果我们能绕过上述问题,我仍然认为 UInt64 将胜过 BigInteger。

编辑:

我刚刚发现:Dirichlet .NET Number Theory Library,它声称 Int128 和 Int256 的性能优于 .Net BigInteger。

它甚至有几个优化的素数分解算法。本可以为我节省 2 天的研究和测试时间。

【问题讨论】:

  • @WDS 再次感谢您在链接的 SO 解决方案中向我指出 Pollard 的 Rho 算法
  • 基本上你不能,但是你可以改变 N 的限制来让它工作。
  • 您不能使用double...它们没有所需的精度。如果 y 是 64 位,则 y^2 最多为 128 位。没有 128 位的整数数据类型。你可以限制自己分解 Int32 数字,那么你不会有任何问题。
  • @CharlesO 好吧,不是真的。
  • (y^2) Mod n - 我不是数学家,但直觉表明这可以通过某种方式进行优化。

标签: .net algorithm biginteger prime-factoring uint64


【解决方案1】:

如何在不溢出的情况下执行模乘(计算平方):

只要模数至少比最大值小一位,解决方案是将数字分成低位和高位两半,然后逐个执行算术运算,有点像小学乘法乘以个位,然后移动总和并乘以十位,依此类推,除了“数字”是整数数据类型中可以表示的最大数的平方根的大小。

考虑使用 8 位算术以 100 为模计算 56 * 37 的示例,因此中间总和不可能是 256 或更大。我们首先表示 a = 56 = 3 * 16 + 8 和 b = 37 = 2 * 16 + 5,(注意 16 是 256 的平方根)所以:

a1 = 8
a2 = 3
b1 = 5
b2 = 2

那么四个中间产品及其班次是:

p11 = 8 * 5 = 40
p12 = 8 * 2 = 16 > 32 > 64 > 128 (28) > 56
p21 = 3 * 5 = 15 > 30 > 60 > 120 (20) > 40
p22 = 3 * 2 = 6 > 12 > 24 > 48 > 96 > 192 (92) > 184 (84) > 168 (68) > 136 (36)

我们使用二进制算术,因此每个数字在移动时都会加倍,并在我们进行时取模 100。两个低半数的乘积不移位,低半数和高半数的乘积移位 4 次(因为 log2 16 = 4),两个高数的乘积-half 数字移位 8 次。然后将中间乘积相加,每次中间和超过 m 时再次删除 m:

s = 40 + 56 = 96
s = 96 + 40 = 136 (36)
s = 36 + 36 = 72

这就是最终答案:56 * 37 = 2072,即 72(模 100)。

如果 m 在整数数据类型的最大值的一位范围内,事情会变得更糟;基本答案是分成三部分,计算中间产品,然后重新组合。

请参阅my blog 了解 Scheme 中的代码,以及使用稍微不同算法的 C 中贡献的解决方案。

【讨论】:

  • 虽然这种部分乘积方法会起作用,但它会大大减慢计算速度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-22
  • 2019-09-08
  • 1970-01-01
  • 2015-08-04
  • 2012-10-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多