在Python中使用大型素数

bnl*_*cas 2 python algorithm primes

使用Python处理大质数的有效方法是什么?你在这里或谷歌上搜索,你会发现许多不同的方法... sieves,primality test algorithms ...哪些方法适用于较大的素数?

bnl*_*cas 7

为了确定一个数字是否是素数,有一个筛子和素数测试.

# for large numbers, xrange will throw an error.
# OverflowError: Python int too large to convert to C long
# to get over this:

def mrange(start, stop, step):
    while start < stop:
        yield start
        start += step

# benchmarked on an old single-core system with 2GB RAM.

from math import sqrt

def is_prime(num):
    if num == 2:
        return True
    if (num < 2) or (num % 2 == 0):
        return False
    return all(num % i for i in mrange(3, int(sqrt(num)) + 1, 2))

# benchmark is_prime(100**10-1) using mrange
# 10000 calls, 53191 per second.
# 60006 function calls in 0.190 seconds.
Run Code Online (Sandbox Code Playgroud)

这似乎是最快的.not any你看到还有另一个版本,

def is_prime(num)
    # ...
    return not any(num % i == 0 for i in mrange(3, int(sqrt(num)) + 1, 2))
Run Code Online (Sandbox Code Playgroud)

然而,在基准测试中,我接受70006 function calls in 0.272 seconds.all 60006 function calls in 0.190 seconds.测试时的使用情况100**10-1.

如果您需要找到下一个最高素数,此方法将不适合您.您需要进行素性测试,我发现Miller-Rabin算法是一个不错的选择.Fermat方法稍微慢一点,但对伪赝假体更准确.使用上述方法在此系统上需要+5分钟.

Miller-Rabin 算法:

from random import randrange
def is_prime(n, k=10):
    if n == 2:
        return True
    if not n & 1:
        return False

    def check(a, s, d, n):
        x = pow(a, d, n)
        if x == 1:
            return True
        for i in xrange(s - 1):
            if x == n - 1:
                return True
            x = pow(x, 2, n)
        return x == n - 1

    s = 0
    d = n - 1

    while d % 2 == 0:
        d >>= 1
        s += 1

    for i in xrange(k):
        a = randrange(2, n - 1)
        if not check(a, s, d, n):
            return False
    return True
Run Code Online (Sandbox Code Playgroud)

Fermat algoithm:

def is_prime(num):
    if num == 2:
        return True
    if not num & 1:
        return False
    return pow(2, num-1, num) == 1
Run Code Online (Sandbox Code Playgroud)

获得下一个最高素数:

def next_prime(num):
    if (not num & 1) and (num != 2):
        num += 1
    if is_prime(num):
        num += 2
    while True:
        if is_prime(num):
            break
        num += 2
    return num

print next_prime(100**10-1) # returns `100000000000000000039`

# benchmark next_prime(100**10-1) using Miller-Rabin algorithm.
1000 calls, 337 per second.
258669 function calls in 2.971 seconds
Run Code Online (Sandbox Code Playgroud)

使用该Fermat测试,我们得到了一个基准测试45006 function calls in 0.885 seconds.,但你的假冒伪劣率更高.

因此,如果只需要检查数字是否为素数,那么第一种方法is_prime就可以了.如果你使用它的话,它是最快的mrange.

理想情况下,您可能希望存储由此生成的素数next_prime并从中读取.

例如,使用next_prime与所述Miller-Rabin算法:

print next_prime(10^301)

# prints in 2.9s on the old single-core system, opposed to fermat's 2.8s
1000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000531
Run Code Online (Sandbox Code Playgroud)

你无法return all(num % i for i in mrange(3, int(sqrt(num)) + 1, 2))及时做到这一点.我甚至不能在这个旧系统上做到这一点.

并且为了确保next_prime(10^301)Miller-Rabin产生正确的值,还使用FermatSolovay-Strassen算法进行了测试.

请参阅:fermat.py,miller_rabin.pysolovay_strassen.pygist.github.com

编辑:修正了一个错误 next_prime

  • 而不是米勒 - 拉宾,你可以使用Baillie-Wagstaff主要测试仪; 它更准确(没有已知的错误),并且,根据您尝试的证人数量,比Miller-Rabin更快,但它确实涉及更多代码.您还可以使用滚轮将计算下一个素数所需的时间减少一半.这两个选项都显示在http://ideone.com/Qf9ZNG上. (2认同)