Rapidement déterminer si un nombre est premier en Python pour les numéros de < 1 milliard de dollars
Mon algorithme actuel de vérifier la primalité des nombres en python est lent pour les nombres entre 10 millions et 1 milliard de dollars. Je veux qu'il soit améliorée, en sachant que je n'obtiendrai jamais de nombre plus grand que 1 milliard de dollars.
Le contexte, c'est que je ne peut pas obtenir une mise en œuvre qui est assez rapide pour la résolution du problème de 60 de projet Euler: je reçois la réponse au problème en 75 secondes où j'en ai besoin en 60 secondes. http://projecteuler.net/index.php?section=problems&id=60
J'ai très peu de mémoire à ma disposition donc je ne peux pas stocker tous les nombres premiers inférieurs à 1 milliard de dollars.
Je suis actuellement en utilisant le standard de la division de première instance à l'écoute avec 6k±1. Est-il rien de mieux que cela? Dois-je déjà besoin pour obtenir le Rabin-Miller méthode pour les nombres qui sont de cette de grands.
primes_under_100 = [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]
def isprime(n):
if n <= 100:
return n in primes_under_100
if n % 2 == 0 or n % 3 == 0:
return False
for f in range(5, int(n ** .5), 6):
if n % f == 0 or n % (f + 2) == 0:
return False
return True
Comment puis-je améliorer cet algorithme?
Précision: je suis nouveau sur python et voudrais travailler avec python 3+.
Code Final
Pour ceux qui sont intéressés, à l'aide de MAK idées, j'ai généré le code suivant qui est d'environ 1/3 plus rapide, ce qui me permet d'obtenir le résultat de la droite d'euler problème en moins de 60 secondes!
from bisect import bisect_left
# sqrt(1000000000) = 31622
__primes = sieve(31622)
def is_prime(n):
# if prime is already in the list, just pick it
if n <= 31622:
i = bisect_left(__primes, n)
return i != len(__primes) and __primes[i] == n
# Divide by each known prime
limit = int(n ** .5)
for p in __primes:
if p > limit: return True
if n % p == 0: return False
# fall back on trial division if n > 1 billion
for f in range(31627, limit, 6): # 31627 is the next prime
if n % f == 0 or n % (f + 4) == 0:
return False
return True
Je la connaissais sous le nom de Python 3 ou Python 3.1, mais il semble que Py3k les références de ces versions.
ne devrait-elle pas être
f
et f+4
...Pourriez-vous le confirmer? pourquoi le 4
?AVERTISSEMENT: L'purement exemple python (premier extrait) ne fonctionne pas pour tous les nombres premiers. La ligne
for f in range(5, int(n ** .5), 6):
devrait être for f in range(5, int(n ** .5) + 1, 6):
; comme il en sort (trop tôt) avant de pouvoir l'afficher que le nombre est divisible par le carré de la racine de lui-même.Je ne sais pas si Daniel fait downvoted vous, mais j'ai trouvé son avertissement utile, j'ai presque utilisé le premier extrait juste parce que j'ai besoin d'une rapide et sale isprime fonction et la deuxième ne fonctionne pas pour moi hors de la boîte sur Python 2.x 😉
OriginalL'auteur Olivier Grégoire | 2010-12-28
Vous devez vous connecter pour publier un commentaire.
Pour des nombres aussi grands que 10^9, une approche peut être de générer tous les nombres premiers jusqu'à sqrt(10^9) et ensuite il suffit de cocher la divisibilité du nombre d'entrée à l'encontre de l'un des numéros dans la liste. Si un nombre n'est pas divisible par un autre premier inférieur ou égal à la racine carrée, il doit lui-même être un premier (il doit avoir au moins un facteur de <=sqrt et un autre >= sqrt à ne pas être le premier). Remarquez comment vous vous n'avez pas besoin de tester la divisibilité pour tous les nombres, juste à la racine carrée (qui est d'environ 32,000 - tout à fait gérable je pense). Vous pouvez générer la liste des nombres premiers en utilisant un tamis.
Vous pouvez également opter pour un probabiliste premier test. Mais ils peuvent être plus difficiles à comprendre, et pour ce problème simplement à l'aide d'une liste de nombres premiers devraient suffire.
si le nombre est inférieure à 32k, utiliser une recherche binaire. à l'aide de la
bisect
module.OriginalL'auteur MAK
Pour résoudre Projet Euler problèmes j'ai fait ce que vous suggérez dans votre question: mettre en Œuvre le Miller Rabin test (en C#, mais je pense qu'elle sera vite en Python). L'algorithme n'est pas que difficile. Pour les numéros ci-dessous 4,759,123,141 il suffit de vérifier qu'un nombre est un solide pseudo-premier pour les bases 2, 7, 61. Combinez cela avec la division de première instance par de petits nombres premiers.
Je ne sais pas comment les problèmes que vous avez résolus à ce jour, mais avoir un rapide test de primalité à votre disposition sera d'une grande valeur pour beaucoup de les problèmes.
Vous devez expérimenter pour trouver une valeur optimale, mais je voudrais commencer par essayer tous les nombres premiers inférieurs à 100. IIRC peut-être même que j'ai fini par sauter la section de première instance pour toutes les valeurs sauf les bases (dans ce cas 2, 7, 61).
Python: s'est Avérée correcte jusqu'à N grand
Eh bien, 4,759,123,141 est très peu... Peut être vérifié en même en divisant par un nombre impair jusqu'à sqrt en un rien de temps. Mais merci pour le lien @Pi - je ne comprends toujours pas pourquoi il n'y a pas de
np.miller_rabin
fonction (ouscipy
si c'est trop scientifique).OriginalL'auteur Peter van der Heijden
Vous pouvez d'abord diviser votre
n
que par votreprimes_under_100
.Aussi, précalculer plus de nombres premiers.
Aussi, vous êtes réellement stocker vos
range()
résultat dans la mémoire - utilisationirange()
au lieu et à l'utilisation de ce mémoire pour l'exécution de Crible d'Eratosthène algorithme.xrange
.Eh bien, je ne suis pas à court de mémoire 😉 Et je suis à l'aide de python 3. Je n'ai jamais vu xrange en python 3.
oui, bien sûr.
xrange est devenue tout simplement de gamme dans la py3k
OriginalL'auteur crazylammer
Bien, j'ai un suivi à mon commentaire sous le (très bon) Peter Van Der Heijden, en réponse à propos de n'être rien de bon pour les très grands nombres premiers (nombres) dans le "populaire" bibliothèques Python. En fait, j'ai mal, il y en a dans
sympy
(grande bibliothèque de l'algèbre symbolique, parmi d'autres):https://docs.sympy.org/latest/modules/ntheory.html#sympy.ntheory.primetest.isprime
Bien sûr, cela peut donner des faux positifs ci-dessus
10**16
, mais c'est déjà beaucoup mieux que tout ce que j'ai pu obtenir à ne rien faire (sauf peut-êtrepip install sympy
😉 )Merci pour la mise à jour! J'ai d'abord lu les sources anciennes de sympy 0.x, et lié à la nouvelle docs. Il ne change pas mon point de sympy est grand, c'est juste mieux que ce que je pensais 😉
En effet, pour la plupart, je pense que c'est la bonne réponse. L'OP est en train de faire du Projet Euler donc ce n'est probablement pas bon pour les problèmes rencontrés par le passé, mais peut-être sympa pour plus tard et, bien entendu, toute autre utilisation pratique.
OriginalL'auteur Tomasz Gandor
Je pense que ce code est le plus rapide ..
Vérifie tous les nombres premiers jusqu'à 1000000 dans 0.42194461822509766 secondes dans ma machine. Pas de boucles , pas d'itérations dans le corps de la fonction
Oui, mais l'inverse n'est pas vrai que tout nombre qui peut être exprimé comme 6n+-1 n'est pas nécessairement un nombre premier. Par exemple, 25 n'est pas un nombre premier (6*4+1).
Merci de trouver le bug! Pardonnez-moi pour perdre votre temps. Je vais modifier le code. Cependant, je suis un autodidacte programmeur amateur
Vous ne pouvez pas réparer la vérification de la divisibilité par 5. Le prochain qui n'est pas un nombre premier est 6*8+1 = 49 = 7*7. Désolé, mais ce n'est pas une méthode qui fonctionne réellement.
OriginalL'auteur Priyadarsan Priyadarsan