# 找到下一个素数的最快代码

17

• 蟒蛇。使用由提供的代码，一个惊人的`357`数字质数`343239883006530485749095039954069660863471765007165270469723172959277159169882802606127982033072727748864815569574042901856099399985832190628701414555752857600000000000000000000000000000000000000002872284792758930912601189043411951050852357613658978971208596097634095500808832510259693761982135208603287199546795000697807728609476163156438356035166156820611`是10秒内的最终数字`primo`。有人会打败这个第一名吗？

@PeterTaylor我认为这个问题与时间复杂度有关。这大约是几秒钟的实用速度。我认为这两件事可能大不相同。
felipa

3
@PeterTaylor让我不同意。最终，网站流量的90％应该来自搜索引擎。谷歌搜索快速半素数分解多项式二次筛返回了原来的问题，我分别从＃2和＃4处获取了代码。我想在某个时候，这个问题的排名也会很高`fast next prime function`
primo

1

mbomb007 '16

21

## Python〜451位数字

my_math.py

``````# legendre symbol (a|m)
# note: returns m-1 if a is a non-residue, instead of -1
def legendre(a, m):
return pow(a, (m-1) >> 1, m)

# strong probable prime
def is_sprp(n, b=2):
d = n-1
s = 0
while d&1 == 0:
s += 1
d >>= 1

x = pow(b, d, n)
if x == 1 or x == n-1:
return True

for r in range(1, s):
x = (x * x)%n
if x == 1:
return False
elif x == n-1:
return True

return False

# lucas probable prime
# assumes D = 1 (mod 4), (D|n) = -1
def is_lucas_prp(n, D):
P = 1
Q = (1-D) >> 2

# n+1 = 2**r*s where s is odd
s = n+1
r = 0
while s&1 == 0:
r += 1
s >>= 1

# calculate the bit reversal of (odd) s
# e.g. 19 (10011) <=> 25 (11001)
t = 0
while s > 0:
if s&1:
t += 1
s -= 1
else:
t <<= 1
s >>= 1

# use the same bit reversal process to calculate the sth Lucas number
# keep track of q = Q**n as we go
U = 0
V = 2
q = 1
# mod_inv(2, n)
inv_2 = (n+1) >> 1
while t > 0:
if t&1 == 1:
# U, V of n+1
U, V = ((U + V) * inv_2)%n, ((D*U + V) * inv_2)%n
q = (q * Q)%n
t -= 1
else:
# U, V of n*2
U, V = (U * V)%n, (V * V - 2 * q)%n
q = (q * q)%n
t >>= 1

# double s until we have the 2**r*sth Lucas number
while r > 0:
U, V = (U * V)%n, (V * V - 2 * q)%n
q = (q * q)%n
r -= 1

# primality check
# if n is prime, n divides the n+1st Lucas number, given the assumptions
return U == 0

# primes less than 212
small_primes = set([
2,  3,  5,  7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97,101,103,107,109,113,
127,131,137,139,149,151,157,163,167,173,
179,181,191,193,197,199,211])

# pre-calced sieve of eratosthenes for n = 2, 3, 5, 7
indices = [
1, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
89, 97,101,103,107,109,113,121,127,131,
137,139,143,149,151,157,163,167,169,173,
179,181,187,191,193,197,199,209]

# distances between sieve values
offsets = [
10, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6,
6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4,
2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6,
4, 2, 4, 6, 2, 6, 4, 2, 4, 2,10, 2]

max_int = 2147483647

# an 'almost certain' primality check
def is_prime(n):
if n < 212:
return n in small_primes

for p in small_primes:
if n%p == 0:
return False

# if n is a 32-bit integer, perform full trial division
if n <= max_int:
i = 211
while i*i < n:
for o in offsets:
i += o
if n%i == 0:
return False
return True

# Baillie-PSW
# this is technically a probabalistic test, but there are no known pseudoprimes
if not is_sprp(n): return False
a = 5
s = 2
while legendre(a, n) != n-1:
s = -s
a = s-a
return is_lucas_prp(n, a)

# next prime strictly larger than n
def next_prime(n):
if n < 2:
return 2
# first odd larger than n
n = (n + 1) | 1
if n < 212:
while True:
if n in small_primes:
return n
n += 2

# find our position in the sieve rotation via binary search
x = int(n%210)
s = 0
e = 47
m = 24
while m != e:
if indices[m] < x:
s = m
m = (s + e + 1) >> 1
else:
e = m
m = (s + e) >> 1

i = int(n + (indices[m] - x))
offs = offsets[m:]+offsets[:m]
while True:
for o in offs:
if is_prime(i):
return i
i += o
``````

``````from time import clock
from my_math import *

n = i = 317**79
while True:
i *= 317
time1 = clock()
n, o = next_prime(i), n
span = clock()-time1
if span > 10:
break
print(len(str(n)), span)
print(o)
``````

``````201 0.13121248650317288
203 0.059535499623555505
206 0.9157767258129175
208 0.2583420518529589
211 0.15367400046653978
213 0.32343915218274955
216 1.3962866788935466
218 0.5986165839513125
221 0.973842206202185
223 2.346910291671148
...
428 0.932809896229827
431 4.345940056627313
433 9.511724255457068
436 6.089835998709333
438 1.3793498894412721
441 4.290633027381972
443 3.5102506044762833
446 3.1629148397352083
448 3.364759208223404
451 7.34668009481652
1551197868099891386459896063244381932060770425565921999885096817830297496627504652115239001983985153119775350914638552307445919773021758654815641382344720913548160379485681746575245251059529720935264144339378936233043585239478807971817857394193701584822359805681429741446927344534491412763713568490429195862973508863067230162660278070962484418979417980291904500349345162151774412157280412235743457342694749679453616265540134456421369622519723266737913
``````

felipa

1
FWIW，在Sage中，`next_prime((2^520)*(10^200))`在我的机器上大约需要15秒钟，因此乍一看脸红了，这是非常令人印象深刻的。但是... `next_prime((2^520)*(10^200),proof=False)`需要0.4秒，因为它仅检查伪素数。当位数超过64时，您的说法“没有已知的伪素数”正在消失，这令人信服。对于357位数字，我什至不敢相信缺乏反例。
2013年

@boothby值得注意的是，这与Maple使用的方法非常相同。该方法已于33年前发布，至今仍没有已知的伪素表示其准确性。
2013年

1

standby

1
@boothby您需要添加一个答案，以便我们在其下进行评论:)
felipa

6

## 具有GMP的C ++：567位

``````#include <gmp.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>

double time() {
struct timeval t;
gettimeofday(&t, NULL);
return t.tv_usec  * 1e-6 + t.tv_sec;
}

int main(int argc, char *argv[]) {
mpz_t n, m;
mpz_init_set_ui(n, 10);
mpz_pow_ui(n, n, 200);
mpz_init(m);
for (int i = 0; true; i++, mpz_mul_ui(n, n, 2)) {
double start = time();
double t = time() - start;
gmp_printf("%d %Zd %f\n", i, m, t);
if (t > 10.0) break;
}
}
``````

`10^200 * 2^1216 + 361`在慢速笔记本电脑上运行一段时间之前，先找到质数（567位）。

3

# 带有GMP模块的Perl，1300位数字

• 一个mod 30轮
• 跟踪剩余的mod 23＃，因此它可以对高达23的素数进行本征模运算
• 对通过这些测试的事物可能进行的主要测试。

• 使用mpz_gcd_ui检查微小的除数（在其中的64位中，最多检查101个）
• 使用单计算的大基数检查小除数。根据输入大小，最多可填10k或40k。
• 对于大于2 ^ 1600的值，请使用Treesieve执行进一步的试验划分。这可以更有效地完成。
• 最终，完成了ES BPSW（以2为底的Miller-Rabin测试，然后再进行一个更强的Lucas测试）。

ES BPSW之前的所有操作都只是优化，当然我们需要next_prime。在Perl中，还可以使用Math :: BigInt模块（在具有可选Pari和GMP后端的内核中）实现next_prime。可以执行AES BPSW（如Pari），但没有进行优化。

``````#!/usr/bin/env perl
use warnings;
use strict;
use Math::Prime::Util ":all";
use Math::Prime::Util::GMP;  # Barf if the backend isn't installed
use Time::HiRes qw(gettimeofday tv_interval);
use Math::GMP;

my \$n = Math::GMP->new(10) ** 200;
while (1) {
my \$start = [gettimeofday];
my \$np = next_prime(\$n);
my \$sec = tv_interval(\$start);
my \$len = length(\$n);
die "next_prime \$len = +",\$np-\$n," in \$sec seconds\n" if \$sec > 10;
warn "  next_prime \$len = +",\$np-\$n," in \$sec seconds\n";
\$n *= 10;
}
``````

2014年

DanaJ

primo 2014年