Este é o melhor algoritmo que eu poderia criar.
def get_primes(n):
numbers = set(range(n, 1, -1))
primes = []
while numbers:
p = numbers.pop()
primes.append(p)
numbers.difference_update(set(range(p*2, n+1, p)))
return primes
>>> timeit.Timer(stmt='get_primes.get_primes(1000000)', setup='import get_primes').timeit(1)
1.1499958793645562
Pode ser feito ainda mais rápido?
Este código tem uma falha: Como numbers
é um conjunto não ordenado, não há garantia de que numbers.pop()
o número mais baixo será removido do conjunto. No entanto, funciona (pelo menos para mim) para alguns números de entrada:
>>> sum(get_primes(2000000))
142913828922L
#That's the correct sum of all numbers below 2 million
>>> 529 in get_primes(1000)
False
>>> 529 in get_primes(530)
True
python
math
optimization
primes
jbochi
fonte
fonte
import antigravity
. Não há nada comorequire 'prime'; Prime.take(10)
(Ruby)?Respostas:
Aviso: os
timeit
resultados podem variar devido a diferenças de hardware ou versão do Python.Abaixo está um script que compara várias implementações:
Muito obrigado a stephan por trazer sieve_wheel_30 à minha atenção. O crédito é para Robert William Hanks por primesfrom2to, primesfrom3to, rwh_primes, rwh_primes1 e rwh_primes2.
Dos métodos simples de Python testados, com psyco , para n = 1000000, rwh_primes1 foi o mais rápido testado.
Dos métodos simples de Python testados, sem psyco , para n = 1000000, rwh_primes2 foi o mais rápido.
De todos os métodos testados, permitindo numpy , para n = 1000000, o número primos de 2 a foi o mais rápido testado.
Os tempos foram medidos usando o comando:
com
{method}
substituído por cada um dos nomes de métodos.primes.py:
A execução dos testes de script que todas as implementações fornecem o mesmo resultado.
fonte
gmpy
- ele tem um bom suporte para números primos, através donext_prime
método de seumpz
tipo.Código Python puro mais rápido e com mais memória:
ou começando com meia peneira
Código numpy mais rápido e com mais memória:
uma variação mais rápida começando com um terço de uma peneira:
Uma versão python pura (difícil de codificar) do código acima seria:
Infelizmente, o python puro não adota a maneira mais simples e rápida de realizar tarefas, e a chamada
len()
dentro do loop[False]*len(sieve[((k*k)//3)::2*k])
é muito lenta. Então eu tive que improvisar para corrigir as entradas (e evitar mais matemática) e fazer alguma mágica matemática extrema (e dolorosa).Pessoalmente, acho uma pena que o numpy (que é tão amplamente usado) não faça parte da biblioteca padrão do Python, e que as melhorias na sintaxe e na velocidade pareçam ser completamente ignoradas pelos desenvolvedores do Python.
fonte
bitarray
- como usado aqui (para o mais simples peneira prime; não um candidato na corrida aqui!) stackoverflow.com/questions/31120986/...primesfrom2to()
transmitir o método, a divisão deve estar dentro dos colchetes?Há uma amostra bastante interessante do Python Cookbook aqui - a versão mais rápida proposta nesse URL é:
então isso daria
Medindo no prompt do shell (como eu prefiro) com este código no pri.py, observei:
parece que a solução Cookbook é duas vezes mais rápida.
fonte
Usando a peneira de Sundaram , acho que quebrei o recorde do puro-Python:
Comparação:
fonte
None
vez da função original funciona e é ainda mais rápido do quezero.__sub__
sundaram3(9)
ele retornará[2, 3, 5, 7, 9]
? Parece fazer isso com numerosos - talvez todos - números ímpares (mesmo quando eles não são primos)O algoritmo é rápido, mas tem uma falha séria:
Você supõe que
numbers.pop()
retornaria o menor número do conjunto, mas isso não é garantido. Os conjuntos são desordenados epop()
remove e retorna um elemento arbitrário ; portanto, não pode ser usado para selecionar o próximo primo dos números restantes.fonte
Para uma solução verdadeiramente mais rápida com N suficientemente grande, seria baixar uma lista pré-calculada de números primos , armazená-la como uma tupla e fazer algo como:
Se
N > primes[-1]
apenas então calcular mais números primos e salvar a nova lista no seu código, da próxima vez será igualmente rápido.Sempre pense fora da caixa.
fonte
Se você não deseja reinventar a roda, pode instalar a biblioteca simbólica de matemática sympy (sim, é compatível com Python 3)
E use a função primerange
fonte
Se você aceitar as ferramentas, mas não estiver entorpecido, aqui está uma adaptação do rwh_primes2 para Python 3 que roda duas vezes mais rápido na minha máquina. A única alteração substancial é usar uma matriz de bytes em vez de uma lista para o booleano e usar compressa em vez de uma compreensão da lista para criar a lista final. (Eu adicionaria isso como um comentário como moarningsun, se eu fosse capaz.)
Comparações:
e
fonte
É instrutivo escrever seu próprio código de busca principal, mas também é útil ter uma biblioteca rápida e confiável à mão. Eu escrevi um wrapper em torno da biblioteca C ++ primesieve , denominada primesieve-python
Tente
pip install primesieve
Eu ficaria curioso para ver a velocidade comparada.
fonte
count_primes
função é muito mais rápida que o #generate_primes
Aqui estão duas versões atualizadas (pura Python 3.6) de uma das funções mais rápidas,
fonte
Implementação determinística do teste de primazia de Miller-Rabin, pressupondo que N <9.080.191
De acordo com o artigo da Wikipedia ( http://en.wikipedia.org/wiki/Miller–Rabin_primality_test ), testar N <9.080.191 para a = 2,3,37 e 73 é suficiente para decidir se N é composto ou não.
E adaptei o código fonte da implementação probabilística do teste original de Miller-Rabin encontrado aqui: http://en.literateprograms.org/Miller-Rabin_primality_test_(Python)
fonte
Se você tem controle sobre N, a maneira mais rápida de listar todos os números primos é pré-calculá-los. A sério. A pré-computação é uma maneira negligenciada de otimização.
fonte
Aqui está o código que eu normalmente uso para gerar números primos em Python:
Ele não pode competir com as soluções mais rápidas postadas aqui, mas pelo menos é python puro.
Obrigado por postar esta questão. Eu realmente aprendi muito hoje.
fonte
Para o código mais rápido, a solução numpy é a melhor. Por razões puramente acadêmicas, porém, estou postando minha versão python pura, que é um pouco menos de 50% mais rápida que a versão do livro de receitas postada acima. Como eu faço a lista inteira na memória, você precisa de espaço suficiente para armazenar tudo, mas parece que está bem dimensionado.
E os resultados:
fonte
Uma implementação ligeiramente diferente de meia peneira usando Numpy:
http://rebrained.com/?p=458
Alguém pode comparar isso com os outros horários? Na minha máquina, parece bastante comparável à outra meia peneira Numpy.
fonte
upto=10**6
:primesfrom2to()
- 7 ms;prime6()
- 12 ms ideone.com/oDg2YEstá tudo escrito e testado. Portanto, não há necessidade de reinventar a roda.
nos dá um recorde de 12,2 ms !
Se isso não for rápido o suficiente, você pode tentar o PyPy:
o que resulta em:
A resposta com 247 up-votes lista 15,9 ms para a melhor solução. Compare isso !!!
fonte
Eu testei algumas funções do unutbu, calculei com o número de milhões de fome
Os vencedores são as funções que usam a biblioteca numpy,
Nota : Também seria interessante fazer um teste de utilização da memória :)
Código de amostra
Código completo no meu repositório github
fonte
Para Python 3
fonte
Peneira principal mais rápida em Pure Python :
Otimizei a Peneira de Eratóstenes para velocidade e memória.
Referência
Resultado
fonte
Primeira vez que utilizei python, alguns dos métodos que utilizo podem parecer um pouco complicados. Acabei de converter meu código c ++ para python e é isso que tenho (embora um pouco lento em python)
fonte
Eu sei que a competição está fechada há alguns anos. …
No entanto, esta é a minha sugestão para uma peneira primária python pura, com base na omissão dos múltiplos de 2, 3 e 5, usando as etapas apropriadas ao processar a peneira. No entanto, é realmente mais lento para N <10 ^ 9 do que as soluções superiores @Robert William Hanks rwh_primes2 e rwh_primes1. Ao usar uma matriz de peneiras ctypes.c_ushort acima de 1,5 * 10 ^ 8, é adaptável aos limites de memória.
10 ^ 6
$ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq (1000000))" 10 loops, o melhor de 3: 46,7 ms por loop
10 ^ 7
$ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq (10000000)" 10 loops, o melhor de 3: 530 ms por loop
10 ^ 8
$ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq (100000000))" 10 loops, o melhor de 3: 5,55 s por loop
10 ^ 9
$ python -mtimeit -s "import primeSieveSpeedComp" "primeSieveSpeedComp.primeSieveSeq (1000000000))" 10 loops, o melhor de 3: 61,2 s por loop
Você pode copiar o código abaixo no ubuntus primeSieveSpeedComp para revisar esses testes.
fonte
Aqui está uma versão numpy do Peneira de Eratóstenes com boa complexidade (menor que a classificação de uma matriz de comprimento n) e vetorização. Comparado aos tempos do @unutbu, é tão rápido quanto os pacotes com 46 microssegundos para encontrar todos os números primos abaixo de um milhão.
Horários:
fonte
Atualizei grande parte do código do Python 3 e o joguei no perfplot (um projeto meu) para ver qual é realmente o mais rápido. Acontece que, para grandes
n
,primesfrom{2,3}to
pegue o bolo:Código para reproduzir o gráfico:
fonte
Meu palpite é que o mais rápido de todos os modos é codificar os primos no seu código.
Então, por que não escrever um script lento que gere outro arquivo de origem que tenha todos os números conectados e importe esse arquivo de origem quando você executar o programa atual.
Obviamente, isso funcionará apenas se você conhecer o limite superior de N em tempo de compilação, mas assim é o caso de (quase) todos os problemas de Euler do projeto.
PS: Eu posso estar errado, embora se a análise da fonte com números primos conectados for mais lenta do que computá-los, mas até onde eu sei, o Python é executado a partir de
.pyc
arquivos compilados, então a leitura de uma matriz binária com todos os números primos até N deve ser sangrenta rápido nesse caso.fonte
Desculpe incomodar, mas erat2 () tem uma falha séria no algoritmo.
Ao procurar o próximo composto, precisamos testar apenas números ímpares. q, p ambos são ímpares; então q + p é par e não precisa ser testado, mas q + 2 * p é sempre ímpar. Isso elimina o teste "if even" na condição do loop while e economiza cerca de 30% do tempo de execução.
Enquanto estamos nisso: em vez do elegante 'D.pop (q, None)', obtenha e exclua o método use 'se q em D: p = D [q], del D [q]', que é duas vezes mais rápido ! Pelo menos na minha máquina (P3-1Ghz). Então, eu sugiro esta implementação deste algoritmo inteligente:
fonte
O método mais rápido que tentei até agora é baseado na função do livro de receitas Python
erat2
:Veja esta resposta para obter uma explicação sobre a aceleração.
fonte
Talvez eu esteja atrasado para a festa, mas terei que adicionar meu próprio código para isso. Ele usa aproximadamente n / 2 no espaço, porque não precisamos armazenar números pares e eu também uso o módulo python bitarray, reduzindo consideravelmente o consumo de memória e permitindo a computação de números primos de até 1.000.000.000
Isso foi executado em um MAC OSX 10.8.3 de 64 bits e 2.4GHZ
fonte
Eu coletei várias peneiras de número primo ao longo do tempo. O mais rápido do meu computador é o seguinte:
fonte
Estou lentamente respondendo a essa pergunta, mas parecia um exercício divertido. Estou usando numpy que pode estar trapaceando e duvido que esse método seja o mais rápido, mas deve ficar claro. Ele peneira uma matriz booleana referente apenas a seus índices e extrai números primos dos índices de todos os valores True. Nenhum módulo necessário.
fonte
ajs_primes3a(10)
->array([2, 3, 5, 7, 9])
.9
não é um primonumpy
soluções mais lentas entre as que retornam uma matriz. Nota: nenhuma implementação verdadeira da Peneira de Eratóstenes usa o módulo - não é necessário mencionar. Você poderia usar emmat[idx*idx::idx]
vez demat[idx*2::idx]
. E aonp.nonzero(mat)[0]
invés denp.where(mat == True)[0]
.Aqui está uma técnica interessante para gerar números primos (ainda não os mais eficientes) usando a compreensão de lista do python:
Você pode encontrar o exemplo e algumas explicações aqui
fonte