Python premier générateur dans une ligne

j'essaie de créer le générateur de nombres premiers dans une ligne de Python juste comme un exercice amusant.

Le code suivant fonctionne comme prévu, mais il est trop lent:

primes = lambda q: (i for i in xrange(1,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,i)])
for i in primes(10):
   print i,

donc j'ai essayé de le faire en vérifiant seulement la racine carrée de j et k:

primes = lambda q: (i for i in xrange(1,q) if i not in [j*k for j in xrange(1,int(round(math.sqrt(i)+1))) for k in xrange(1,int(round(math.sqrt(i)+1)))])
for i in primes(10):
   print i,

mais il produit: 2 3 5 6 7 8

donc il doit y avoir un problème avec mes indices j et k, mais je n'en ai pas la moindre idée.

5
demandé sur Zero Piraeus 2012-05-17 20:41:19

4 réponses

ce n'est pas le Crible d'Eratosthène, même si on dirait qu'il est. Il est en fait bien pire. Le tamis est le meilleur algorithme pour trouver des nombres premiers.

voir http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

edit : j'ai modifié https://stackoverflow.com/a/9302299/711085 pour être une doublure (à l'origine ce N'était pas le vrai tamis, mais maintenant il l'est... probablement...):

reduce( (lambda r,x: r-set(range(x**2,N,x)) if (x in r) else r), 
        range(2,N), set(range(2,N)))

Démo:

>>> primesUpTo(N): lambda N: reduce(...)
>>> primesUpTo(30)
{2, 3, 5, 7, 11, 13, 17, 19}

malheureusement, je pense que bien que ce serait efficace dans un langage de programmation fonctionnel, il pourrait ne pas être aussi efficace en python en raison de non-persistance (état partagé et immuable) des structures de données, et tout tamis en python aurait besoin d'utiliser la mutation pour atteindre des performances comparables. On peut encore le mettre dans une doublure si on le veut désespérément. Mais d'abord...

tamis Normal:

>>> N = 100
>>> table = list(range(N))
>>> for i in range(2,int(N**0.5)+1):
...     if table[i]:
...         for mult in range(i**2,N,i):
...             table[mult] = False
... 
>>> primes = [p for p in table if p][1:]
>>> 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]

nous pouvons maintenant définir et appeler des fonctions anonymes sur la même ligne, ainsi que le hack de [...].__setitem__ pour faire la mutation en ligne, et le hack de ... and foo pour évaluer ... tout en retournant foo :

>>> primesUpTo = lambda N: (lambda table: [[table.__setitem__(mult,False) for mult in range(i**2,N,i)] for i in range(2,int(N**0.5)+1) if table[i]] and [p for p in table if p][1:])(list(range(N)))
>>> primesUpTo(30)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Procéder à grincer des dents dans l'horreur, le one-liner élargi (bizarrement belle parce que vous pourrait presque traduire directement les flux de contrôle, mais un terrible abus de tout):

lambda N:
    (lambda table: 
        [[table.__setitem__(mult,False) for mult in range(i**2,N,i)] 
            for i in range(2,int(N**0.5)+1) if table[i]] 
        and [p for p in table if p][1:]
    )(list(range(N)))

cette version mutante à une doublure a abandonné à environ 10 8 sur ma machine, tandis que la version mutante originale a abandonné à environ 10 9 , s'épuisant de mémoire (curieusement).

L'original reduce version a donné jusqu'à 10 7 . Alors peut-être ce n'est pas que inefficace après tout (au moins pour les nombres que vous pouvez traiter avec sur votre ordinateur).

edit2 Il semble que vous pouvez les abus effets secondaires de façon plus concise:

reduce( (lambda r,x: (r.difference_update(range(x**2,N,x)) or r)
                     if (x in r) else r), 
        range(2,N), set(range(2,N)))

, Elle donne l'ordre de 10 8 , de même que le one-liner mutation version.

edit3: Ce pistes O(N) empiriques de la complexité , alors que sans l' difference_update , il a couru à O(n^2.2) de la complexité .

limitation de la portée qui est réduite au-delà, à la rqrt de la limite supérieure, et de travail avec des cotes seulement , les deux donnent lieu à des vitesses supplémentaires ( 2x et 1,6 x correspondant):

reduce( (lambda r,x: (r.difference_update(range(x*x,N,2*x)) or r)
                     if (x in r) else r), 
        range(3, int((N+1)**0.5+1), 2),
        set([2] + range(3,N,2)))
11
répondu ninjagecko 2017-05-23 12:26:23

vous ne pouvez pas vérifier les produits des nombres seulement jusqu'à la racine carrée pour tester pour une prime. Regardez 8 - la racine carrée de 8 est 2.8, donc il ne va jamais essayer 4 * 2. (En effet, les seuls nombres que ne serait pas être considérés comme des nombres premiers sont des nombres carrés).

ETA: au lieu d'essayer toutes les combinaisons possibles de j et k, pourquoi ne pas vérifier si je suis divisible par chaque j (en utilisant i % j == 0 ) jusqu'à la racine carrée de j? Cela prend moins de code et est beaucoup plus efficace (bien qu'il soit encore pas presque aussi efficace que le tamis D'Eratosthène).

4
répondu David Robinson 2012-05-17 16:57:28

voilà ce que vous vouliez:

def primes (q) :
 # return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,i)])
 # return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,j+1)])
 # return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i/2+1) for k in xrange(1,j+1)])

 return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i/2+1) for k in xrange(1,min(j+1,i/j+1))])

à Haskell, les fourchettes sont inclusives, donc primes(542) est

[n | n<-[2..541], not $ elem n [j*k | j<-[1..n-1],     k<-[1..n-1]]]  --  25.66s
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n-1],     k<-[1..j]]]    --  15.30s
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n`div`2], k<-[1..j]]]    --   6.00s
                                                                      --   0.79s
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n`div`2], k<-[1..min j (n`div`j)]]] 

et effectivement, 1*x == x donc 1 n'est pas nécessaire comme un multiplicateur, donc il devrait être

[n | n<-[2..541], not $ elem n [j*k | j<-[2..n`div`2], k<-[2..min j (n`div`j)]]] 

qui prend seulement 0.59 secondes. Ou, en Python,

def primes (q) :
 return (i for i in xrange(2,q) if i not in [j*k for j in xrange(2,i/2+1) for k in xrange(2,min(j+1,i/j+1))])

mise à jour: pour une raison quelconque, min j ... ne fait pas une grande différence, du moins à Haskell. Ainsi l'expression devient simplement

[n | n<-[2..541], not $ elem n [j*k | j<-[2..n`div`2], k<-[2..n`div`j]]] 
2
répondu Will Ness 2015-12-16 10:29:49

Que Diriez-vous de:

def primes(x):
  return [i for i in range(2,x) if 0 not in [i%j for j in range(2,i)]]
0
répondu Questions 2017-07-23 04:56:02