【问题标题】:Why classical division ("/") for large integers is much slower than integer division ("//")?为什么大整数的经典除法(“/”)比整数除法(“//”)慢得多?
【发布时间】:2021-05-21 23:18:58
【问题描述】:

我遇到了一个问题:如果对 (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, 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

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))

【问题讨论】:

  • 那么输入是什么?
  • 不管为什么,我什至不会考虑使用纯Python来实现一段高性能代码。原生库或类似 Cython 的库的性能可以比单独使用 Python 好数千倍。 Python 易于阅读和编写,但它的速度并不快。
  • 准确显示您运行的内容。 pow(a, (p - 1) / 2, p) 应该引发异常:TypeError: pow() 3rd argument not allowed unless all arguments are integers
  • @superbrain 不确定 Cython。此外,每个单独的划分都可能由 C 完成,但需要转换和设置浮动对象。 Python浮点除法的源码在这里:github.com/python/cpython/blob/master/Objects/…
  • @RandomDavis 你确定速度差异的真正原因不是/ 给出了错误的结果,对算法产生了负面影响吗?

标签: python division integer-division floor-division


【解决方案1】:

如 cmets 中所述,如果 p 是超过 53 位的整数,int((p - 1) / 2) 会产生垃圾。转换为浮点数进行除法时,仅保留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 次尝试中找到了一个可能的素数。

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

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。

【讨论】:

    猜你喜欢
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 2013-06-23
    相关资源
    最近更新 更多