微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么大整数的经典除法“/”比整数除法“//”慢得多?

如何解决为什么大整数的经典除法“/”比整数除法“//”慢得多?

我遇到了一个问题:如果您对 (p-1)/2 使用经典除法,那么对于 512 位奇数,代码会非常慢。但是通过楼层划分,它可以立即生效。是浮点转换引起的吗?

def solovayStrassen(p,iterations):    
    for i in range(iterations):         
        a = random.randint(2,p - 1)         
        if gcd(a,p) > 1:
            return False        
        first = pow(a,int((p - 1) / 2),p)    
        j = (Jacobian(a,p) + p) % p                            
        if first != j: 
            return False 
    return True

完整代码

import random
from math import gcd

#Jacobian symbol
def Jacobian(a,n): 
    if (a == 0): 
        return 0 
    ans = 1 
    if (a < 0):         
        a = -a
        if (n % 4 == 3): 
            ans = -ans 
    if (a == 1): 
        return ans
    while (a): 
        if (a < 0): 
            a = -a
            if (n % 4 == 3):
                ans = -ans 
        while (a % 2 == 0): 
            a = a // 2 
            if (n % 8 == 3 or n % 8 == 5): 
                ans = -ans  
        a,n = n,a
        if (a % 4 == 3 and n % 4 == 3): 
            ans = -ans
        a = a % n
        if (a > n // 2): 
            a = a - n 
    if (n == 1): 
        return ans
    return 0 

def solovayStrassen(p,p) + p) % p                            
        if first != j: 
            return False 
    return True

def findFirstPrime(n,k): 
    while True:              
        if solovayStrassen(n,k):            
            return n
        n+=2    

a = random.getrandbits(512)
if a%2==0:
    a+=1    
print(findFirstPrime(a,100))

解决方法

如注释中所述,如果 int((p - 1) / 2) 是一个超过 53 位的整数,则 p 会产生垃圾。为除法转换为浮点数时,仅保留 p-1 的前 53 位。

>>> p = 123456789123456789123456789
>>> (p-1) // 2
61728394561728394561728394
>>> hex(_)
'0x330f7ef971d8cfbe022f8a'
>>> int((p-1) / 2)
61728394561728395668881408
>>> hex(_) # lots of trailing zeroes
'0x330f7ef971d8d000000000'

当然,素性测试的基础理论依赖于完全使用(p-1)/2的无限精确值,而不是某些近似值或多或少仅适用于前 53 个最重要的值位。

正如评论中所指出的,使用垃圾很可能会使这部分返回得更早,而不是更晚:

        if first != j: 
            return False 

那么为什么它总体上要慢得多?因为 findFirstPrime() 必须多次调用 solovayStrassen() 才能找到纯粹靠运气的垃圾。

要看到这一点,请更改代码以显示循环尝试的频率:

def findFirstPrime(n,k):
    count = 0
    while True:
        count += 1
        if count % 1000 == 0:
            print(f"at count {count:,}")
        if solovayStrassen(n,k):            
            return n,count
        n+=2    

然后添加,例如

random.seed(12)

在主程序开始时,这样您就可以获得可重现的结果。

使用楼层(//)除法,运行速度相当快,显示

(6170518232878265099306454685234429219657996228748920426206889067017854517343512513954857500421232718472897893847571955479036221948870073830638539006377457,906)

所以它在第 906 次尝试中找到了一个可能的素数。

但是使用浮动 (/) 除法,我从未见过它靠运气成功:

at count 1,000
at count 2,000
at count 3,000
...
at count 1,000,000

然后放弃 - “垃圾进,垃圾出”。

还有一点要注意,顺便说一下:+ p in:

        j = (Jacobian(a,p) + p) % p                            

j 的值没有影响。对? p % p 为 0。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。