我正在尝试使用python计算Pollard rho
数,计算非常长的整数,例如小于1的整数
65779646778470582047547160396995720887221575959770627441205850493179860146690755880473849736210807898494458426111244201404810495587574110361900128405354081638434164434968839614760264675889940272767106444249
我已经尝试在我的intel core i9 10980HK
CPU上进行计算,这会导致几分钟的高负载工作,但没有任何成功。我试图使用numba
和@njit
装饰器来连接RTX 2070 super
(在笔记本电脑上),但它给出了以下错误
- argument 0: Int value is too large:
代码如下:
import numpy as np
import datetime
def pgcd(a,b):
if b==0:
return a
else:
r=a%b
return pgcd(b,r)
def pollardrho(n):
f = lambda z: z*z+1
x, y, d = 1, 1, 1
c = 0
while d==1:
c += 1
x = f(x) % n
y = f(f(y)) % n
d = pgcd(y-x, n)
return d, c
def test_time(n):
t = datetime.datetime.now()
d, c = pollardrho(int(n))
tps = datetime.datetime.now() - t
print(tps, c, d)
file = open("some_powersmooths_large.txt", "r")
for line in file.readlines():
if not line.startswith("#"):
print(line)
print("\n")
test_time(line)
print("\n")
我如何处理这种类型的大数计算
第1部分(第2部分,见下文第2部分)
Numba最多只能处理64位整数,它没有大整数算法,只有Python有。作为Numba promise的开发者,大整数将在未来的版本中得到支持。您需要大整数算术,因为您的输入和计算中有非常大的整数
一个优化建议是使用GMPY2Python库。它是高度优化的长算术库,比长算术的常规Python实现快得多。例如,对于非常大的整数,它使用Fast Fourier Transform实现乘法,这是最快的乘法算法
但是安装GMPY2可能有点困难。Windows的最新预编译版本在by this link上可用。下载Python版本的.whl文件并通过pip安装,例如,对于我的Windows 64位Python 3.7,我下载并安装了
pip install gmpy2-2.0.8-cp37-cp37m-win_amd64.whl
。对于Linux,最容易通过sudo apt install -y python3-gmpy2
安装使用GMPY2后,您的代码将尽可能快,因为此库代码几乎是世界上最快的。即使是Numba(如果它有很长的算术)也不会有更多的改进。只有更快的公式和更好的算法才有助于进一步改进或减小输入整数
但是,即使使用GMPY2,您的示例中的大整数对于您的算法来说也是一种变大的方法。您必须选择较小的整数或更快的算法。我已经运行了你的算法和数字5分钟或更长时间,但没有得到结果。但如果使用常规Python之前的结果是在1小时内,那么在使用GMPY2之后,它可能在10分钟或更快的时间内完成
也不是很确定,但可能在您的算法中
f(f(y)) % n
应该等价于f(f(y) % n) % n
,这应该计算得更快,因为它将做两倍短的乘法。但这需要额外检查此外,您的大整数似乎是素数,正如基于Primo椭圆曲线的素数证明程序所证明的,它在我的电脑上3秒钟内证明了这个整数的素数。Primo只证明素数(100%保证),但不计算数(分解为除数)。分解数字可以由程序from this list完成,这些程序实现已知最快的分解算法,如果一些链接失效,那么用谷歌搜索这些程序的名称
只需将所有整数
n
包装成gmpy2.mpz(n)
。例如,我对您的代码进行了一些改进,将其包装到gmpy2.mpz()
中,并创建了一个循环,以便打印所有除数。同样作为一个例子,我不是拿你的大素数,而是一个小得多的Pi的前25位数字,它是复合的,它的所有除数都在我的电脑上以7秒的时间打印出来:Try it online!
输出:
第二部分
阅读维基百科的文章(here和here),我决定实现一个更快版本的Pollard Rho算法
下面实现的我的版本看起来更复杂,但除法和乘法减少了两倍,平均而言,循环的迭代次数也减少了一倍
与我的笔记本电脑上运行时间为
7
分钟的原始OP算法相比,这种改进导致我的测试用例的运行时间为3
分钟我的算法也是随机的,这意味着它不使用见证1或见证2,而是在范围
[1, N - 2]
内使用随机见证。在极少数情况下,它可能会像维基百科中所说的那样失败,然后我用不同的见证人重新运行算法。此外,它还使用Fermat primality test检查输入的数字是否为素数,然后不搜索更多的除数对于测试,我使用了由代码
p = 1; for i in range(256): p *= random.randrange(2, 1 << 32)
生成的输入数p
,基本上它最多由256
个因子组成我还改进了这两种算法以输出更多的统计数据。其中一个统计参数是
pow
,它显示了每个步骤的复杂性,pow为0.25表示如果除数是d
,那么当前的分解步骤花费了c = d^0.25
次迭代来找到这个除数d
。正如维基百科Pollard所说-Rho算法平均应具有pow = 0.25
,这意味着查找任何除数d
的复杂度(迭代次数)约为d^0.25
在接下来的代码中,还有其他一些改进,比如提供了大量的统计数据。找到循环中的所有因素
我的测试用例的算法版本的平均pow为
0.24
,而以前的原始版本为0.3
。较小的pow意味着平均进行较少的循环迭代还测试了我的版本是否使用GMPY2。显然,GMPY2与常规Python大整数算法相比并没有太大的改进,主要是因为GMPY2对真正的大数字(上万位)(使用快速傅立叶变换乘法等)进行了优化,而在我的测试中,这个数字并不是太大。但是GMPY2仍然提供了大约
1.35x
倍的加速,提供了3
分钟的运行时间,而对于相同的算法,没有GMPY2的情况下几乎是4
分钟。要使用或不使用gmpy2进行测试,只需将def num(n)
函数内部更改为return gmpy2.mpz(n)
或return n
Try it online!
输出:
PS。还决定实现Pollard Rho分解算法的极简但快速版本,纯Pythonic,准备复制粘贴到任何项目中(例如分解Pi的前25位数字):
Try it online!
考虑到
pollardrho
中的操作非常低效,我对该操作需要一段时间并不感到惊讶。 然而,我不知道这个特定的函数,所以我不知道它是否可以变得更有效在Python中,整数具有任意长度。 这意味着它们可以是任意长度的,Python本身将使用64位整数(通过将它们分散到多个整数上)正确地存储它们。 (您可以自己测试一下,例如创建一个不能存储在64位无符号整数中的整数,如
a = 2**64
,然后检查a.bit_length()
方法的输出,该方法应该是65
)所以,从理论上讲,你应该能够计算任何整数。 但是,由于您使用的是Numba,因此由于Numba的工作方式,您仅限于可以实际存储在64位无符号整数中的整数。 您得到的错误只是数字变得太大,无法存储在64位无符号整数中
底线:没有Numba,你可以很好地计算这个数字。有了麻木,你就不能。 当然,如果您只想大致知道数字是什么,而不想知道精确的数字,那么您可以使用浮点数
相关问题 更多 >
编程相关推荐