RPython (PyPy 4.0.1), 4032
RPython è un sottoinsieme limitato di Python, che può essere tradotto in C e quindi compilato usando RPython Toolchain. Il suo scopo espresso è di aiutare nella creazione di interpreti linguistici, ma può anche essere usato per compilare semplici programmi.
Compilare, scarica l'attuale sorgente PyPy (PyPy 4.0.1) ed esegui quanto segue:
$ pypy /pypy-4.0.1-src/rpython/bin/rpython --opt=3 good-primes.py
L'eseguibile risultante verrà nominato good-primes-c
o simile nella directory di lavoro corrente.
Note di implementazione
Il generatore di numeri primi primes
è un setaccio di Eratostene senza limiti, che utilizza una ruota per evitare multipli di 2 , 3 , 5 o 7 . Si chiama anche ricorsivamente per generare il valore successivo da utilizzare per la marcatura. Sono abbastanza soddisfatto di questo generatore. La profilatura delle linee rivela che le due linee più lente sono:
37> n += o
38> if n not in sieve:
quindi non credo che ci sia molto margine di miglioramento, oltre a usare forse una ruota più grande.
Per il controllo "di bontà", per prima cosa tutti i fattori di due vengono rimossi da n-1 , usando un hack bit-twiddling per trovare la più grande potenza di due che è un divisore (n-1 & 1-n)
. Poiché p-1 è necessariamente anche per qualsiasi p primo > 2 , ne consegue che 2 deve essere uno dei fattori primi distinti. Ciò che rimane viene inviato alla is_prime_power
funzione, che fa ciò che implica il suo nome. Controllare se un valore è una potenza primaria è "quasi libero", poiché viene eseguito contemporaneamente al controllo di primalità, con al massimo operazioni O (log p n) , dove p è il fattore primo più piccolo di n. La divisione di prova potrebbe sembrare un po 'ingenua, ma dai miei test è il metodo più veloce per valori inferiori a 2 32 . Risparmio un po 'riutilizzando la ruota dal setaccio. In particolare:
59> while p*p < n:
60> for o in offsets:
iterando su una ruota della lunghezza 48, il p*p < n
controllo verrà ignorato migliaia di volte, al prezzo basso e basso di non più di 48 operazioni di modulo aggiuntive. Salta anche oltre il 77% di tutti i candidati, anziché il 50% prendendo solo le probabilità.
Le ultime uscite sono:
3588 (987417437 - 987413849) 60.469000s
3900 (1123404923 - 1123401023) 70.828000s
3942 (1196634239 - 1196630297) 76.594000s
4032 (1247118179 - 1247114147) 80.625000s
4176 (1964330609 - 1964326433) 143.047000s
4224 (2055062753 - 2055058529) 151.562000s
Il codice è anche valido Python e dovrebbe raggiungere 3588 ~ 3900 quando eseguito con un interprete PyPy recente.
# primes less than 212
small_primes = [
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
# distances between sieve values, starting from 211
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]
# tabulated, mod 105
dindices =[
0,10, 2, 0, 4, 0, 0, 0, 8, 0, 0, 2, 0, 4, 0,
0, 6, 2, 0, 4, 0, 0, 4, 6, 0, 0, 6, 0, 0, 2,
0, 6, 2, 0, 4, 0, 0, 4, 6, 0, 0, 2, 0, 4, 2,
0, 6, 6, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 4, 2,
0, 6, 2, 0, 4, 0, 0, 4, 6, 0, 0, 2, 0, 6, 2,
0, 6, 0, 0, 4, 0, 0, 4, 6, 0, 0, 2, 0, 4, 8,
0, 0, 2, 0,10, 0, 0, 4, 0, 0, 0, 2, 0, 4, 2]
def primes(start = 0):
for n in small_primes[start:]: yield n
pg = primes(6)
p = pg.next()
q = p*p
sieve = {221: 13, 253: 11}
n = 211
while True:
for o in offsets:
n += o
stp = sieve.pop(n, 0)
if stp:
nxt = n/stp
nxt += dindices[nxt%105]
while nxt*stp in sieve: nxt += dindices[nxt%105]
sieve[nxt*stp] = stp
else:
if n < q:
yield n
else:
sieve[q + dindices[p%105]*p] = p
p = pg.next()
q = p*p
def is_prime_power(n):
for p in small_primes:
if n%p == 0:
n /= p
while n%p == 0: n /= p
return n == 1
p = 211
while p*p < n:
for o in offsets:
p += o
if n%p == 0:
n /= p
while n%p == 0: n /= p
return n == 1
return n > 1
def main(argv):
from time import time
t0 = time()
m = 0
p = q = 7
pgen = primes(3)
for n in pgen:
d = (n-1 & 1-n)
if is_prime_power(n/d):
p, q = q, n
if q-p > m:
m = q-p
print m, "(%d - %d) %fs"%(q, p, time()-t0)
return 0
def target(*args):
return main, None
if __name__ == '__main__':
from sys import argv
main(argv)
RPython (PyPy 4.0.1), 22596
Questa presentazione è leggermente diversa dalle altre finora pubblicate, in quanto non controlla tutti i numeri primi buoni, ma fa invece salti relativamente grandi. Uno svantaggio nel fare questo è che i setacci non possono essere usati [sto corretto?] , Quindi bisogna fare affidamento interamente sul test di primalità che in pratica è un po 'più lento. C'è anche un mezzo felice tra il tasso di crescita e il numero di valori controllati ogni volta. I valori più piccoli sono molto più veloci da controllare, ma i valori più grandi hanno maggiori probabilità di avere lacune maggiori.
Per placare gli dei matematici, ho deciso di seguire una sequenza simile a Fibonacci, avendo il prossimo punto di partenza come la somma dei due precedenti. Se non vengono trovati nuovi record dopo aver verificato 10 coppie, lo script si sposta sul successivo.
Le ultime uscite sono:
6420 (12519586667324027 - 12519586667317607) 0.364000s
6720 (707871808582625903 - 707871808582619183) 0.721000s
8880 (626872872579606869 - 626872872579597989) 0.995000s
10146 (1206929709956703809 - 1206929709956693663) 4.858000s
22596 (918415168400717543 - 918415168400694947) 8.797000s
Quando vengono compilati, vengono utilizzati numeri interi a 64 bit, sebbene in alcuni punti si supponga che due numeri interi possano essere aggiunti senza overflow, quindi in pratica sono utilizzabili solo 63. Al raggiungimento di 62 bit significativi, il valore corrente viene dimezzato due volte, per evitare un overflow nel calcolo. Il risultato è che lo script mescola i valori nell'intervallo 2 60 - 2 62 . Non superare la precisione del numero intero nativo rende lo script più veloce quando interpretato.
Il seguente script PARI / GP può essere utilizzato per confermare questo risultato:
isgoodprime(n) = isprime(n) && omega(n-1)==2
for(n = 918415168400694947, 918415168400717543, {
if(isgoodprime(n), print(n" is a good prime"))
})
try:
from rpython.rlib.rarithmetic import r_int64
from rpython.rtyper.lltypesystem.lltype import SignedLongLongLong
from rpython.translator.c.primitive import PrimitiveType
# check if the compiler supports long long longs
if SignedLongLongLong in PrimitiveType:
from rpython.rlib.rarithmetic import r_longlonglong
def mul_mod(a, b, m):
return r_int64(r_longlonglong(a)*b%m)
else:
from rpython.rlib.rbigint import rbigint
def mul_mod(a, b, m):
biga = rbigint.fromrarith_int(a)
bigb = rbigint.fromrarith_int(b)
bigm = rbigint.fromrarith_int(m)
return biga.mul(bigb).mod(bigm).tolonglong()
# modular exponentiation b**e (mod m)
def pow_mod(b, e, m):
r = 1
while e:
if e&1: r = mul_mod(b, r, m)
e >>= 1
b = mul_mod(b, b, m)
return r
except:
import sys
r_int64 = int
if sys.maxint == 2147483647:
mul_mod = lambda a, b, m: a*b%m
else:
mul_mod = lambda a, b, m: int(a*b%m)
pow_mod = pow
# legendre symbol (a|m)
# note: returns m-1 if a is a non-residue, instead of -1
def legendre(a, m):
return pow_mod(a, (m-1) >> 1, m)
# strong probable prime
def is_sprp(n, b=2):
if n < 2: return False
d = n-1
s = 0
while d&1 == 0:
s += 1
d >>= 1
x = pow_mod(b, d, n)
if x == 1 or x == n-1:
return True
for r in xrange(1, s):
x = mul_mod(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):
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 = r_int64(0)
while s:
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:
if t&1:
# U, V of n+1
U, V = mul_mod(inv_2, U + V, n), mul_mod(inv_2, V + mul_mod(D, U, n), n)
q = mul_mod(q, Q, n)
t -= 1
else:
# U, V of n*2
U, V = mul_mod(U, V, n), (mul_mod(V, V, n) - 2 * q) % n
q = mul_mod(q, q, n)
t >>= 1
# double s until we have the 2**r*sth Lucas number
while r:
U, V = mul_mod(U, V, n), (mul_mod(V, V, n) - 2 * q) % n
q = mul_mod(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 = [
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]
bit_lengths = [
0x00000000, 0x00000001, 0x00000003, 0x00000007,
0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF]
max_int = 2147483647
# returns the index of x in a sorted list a
# or the index of the next larger item if x is not present
# i.e. the proper insertion point for x in a
def binary_search(a, x):
s = 0
e = len(a)
m = e >> 1
while m != e:
if a[m] < x:
s = m
m = (s + e + 1) >> 1
else:
e = m
m = (s + e) >> 1
return m
def log2(n):
hi = n >> 32
if hi:
return binary_search(bit_lengths, hi) + 32
return binary_search(bit_lengths, n)
# integer sqrt of n
def isqrt(n):
c = n*4/3
d = log2(c)
a = d>>1
if d&1:
x = r_int64(1) << a
y = (x + (n >> a)) >> 1
else:
x = (r_int64(3) << a) >> 2
y = (x + (c >> a)) >> 1
if x != y:
x = y
y = (x + n/x) >> 1
while y < x:
x = y
y = (x + n/x) >> 1
return x
# integer cbrt of n
def icbrt(n):
d = log2(n)
if d%3 == 2:
x = r_int64(3) << d/3-1
else:
x = r_int64(1) << d/3
y = (2*x + n/(x*x))/3
if x != y:
x = y
y = (2*x + n/(x*x))/3
while y < x:
x = y
y = (2*x + n/(x*x))/3
return x
## Baillie-PSW ##
# this is technically a probabalistic test, but there are no known pseudoprimes
def is_bpsw(n):
if not is_sprp(n, 2): return False
# idea shamelessly stolen from Mathmatica's PrimeQ
# if n is a 2-sprp and a 3-sprp, n is necessarily square-free
if not is_sprp(n, 3): return False
a = 5
s = 2
# if n is a perfect square, this will never terminate
while legendre(a, n) != n-1:
s = -s
a = s-a
return is_lucas_prp(n, a)
# an 'almost certain' primality check
def is_prime(n):
if n < 212:
m = binary_search(small_primes, n)
return n == small_primes[m]
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:
p = 211
while p*p < n:
for o in offsets:
p += o
if n%p == 0:
return False
return True
return is_bpsw(n)
# 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:
m = binary_search(small_primes, n)
return small_primes[m]
# find our position in the sieve rotation via binary search
x = int(n%210)
m = binary_search(indices, x)
i = r_int64(n + (indices[m] - x))
# adjust offsets
offs = offsets[m:] + offsets[:m]
while True:
for o in offs:
if is_prime(i):
return i
i += o
# true if n is a prime power > 0
def is_prime_power(n):
if n > 1:
for p in small_primes:
if n%p == 0:
n /= p
while n%p == 0: n /= p
return n == 1
r = isqrt(n)
if r*r == n:
return is_prime_power(r)
s = icbrt(n)
if s*s*s == n:
return is_prime_power(s)
p = r_int64(211)
while p*p < r:
for o in offsets:
p += o
if n%p == 0:
n /= p
while n%p == 0: n /= p
return n == 1
if n <= max_int:
while p*p < n:
for o in offsets:
p += o
if n%p == 0:
return False
return True
return is_bpsw(n)
return False
def next_good_prime(n):
n = next_prime(n)
d = (n-1 & 1-n)
while not is_prime_power(n/d):
n = next_prime(n)
d = (n-1 & 1-n)
return n
def main(argv):
from time import time
t0 = time()
if len(argv) > 1:
n = r_int64(int(argv[1]))
else:
n = r_int64(7)
if len(argv) > 2:
limit = int(argv[2])
else:
limit = 10
m = 0
e = 1
q = n
try:
while True:
e += 1
p, q = q, next_good_prime(q)
if q-p > m:
m = q-p
print m, "(%d - %d) %fs"%(q, p, time()-t0)
n, q = p, n+p
if log2(q) > 61:
q >>= 2
e = 1
q = next_good_prime(q)
elif e > limit:
n, q = p, n+p
if log2(q) > 61:
q >>= 2
e = 1
q = next_good_prime(q)
except KeyboardInterrupt:
pass
return 0
def target(*args):
return main, None
if __name__ == '__main__':
from sys import argv
main(argv)