Seguindo a boa tradição de perguntas como Encontrar o maior número primo cujo comprimento, soma e produto são números primos , essa é uma variante do maior desafio.
Entrada
Seu código não deve receber nenhuma entrada.
Definição
Dizemos que um primo p
é good
se p-1
tiver 2
fatores primos exatamente distintos.
Saída
Seu código deve gerar a diferença absoluta entre bons primos consecutivos q
e, p
portanto |q-p|
, o maior possível e q
o menor primo bom maior que p
. Você pode produzir qualquer número de bons pares e sua última saída será tomada como a pontuação.
Exemplo
A sequência dos 55 primeiros números primos bons é https://oeis.org/A067466 .
Ponto
Sua pontuação é simplesmente |q-p|
para o par de primos bons que você produz.
Línguas e bibliotecas
Você pode usar qualquer idioma ou biblioteca que desejar (que não foi projetada para esse desafio), exceto as funções de biblioteca para teste de primalidade ou fatoração de números inteiros. No entanto, para fins de pontuação, executarei seu código na minha máquina, portanto, forneça instruções claras sobre como executá-lo no Ubuntu.
Minha máquina Os horários serão executados na minha máquina. Esta é uma instalação padrão do Ubuntu em um processador de 8 GB AMD FX-8350 de oito núcleos. Isso também significa que eu preciso poder executar seu código.
Detalhes
- Matarei seu código após 2 minutos, a menos que ele comece a ficar sem memória antes disso. Portanto, certifique-se de produzir algo antes do corte.
- Você não pode usar nenhuma fonte externa de números primos.
- Você pode usar métodos probabilísticos de teste principal, embora Mego me diga que, com boas tabelas, Miller-Rabin pode testar até 341.550.071.728.321 (ou até mais) deterministicamente. Veja também http://miller-rabin.appspot.com/ .
Melhores entradas que verificam todos os números inteiros de 1
- 756 por gato em Go
- 756 por El'endia Starman em Python
- 1932 por Adnan em C # (usando o mono 3.2.8)
- 2640 por yeti em Python (usando pypy 4.01)
- 2754 por Reto Koradi em C ++
- 3486 por Peter Taylor em Java
- 3900 por primo no RPython (usando pypy 4.01)
- 4176 por The Coder em Java
Melhores entradas que podem pular um grande número de números inteiros para encontrar uma grande lacuna
- 14226 por Reto Koradi em C ++
- 22596 por primo no RPython (usando o pypy 4.01). Registro alcançado após 5 segundos!
fonte
Respostas:
RPython (PyPy 4.0.1), 4032
O RPython é um subconjunto restrito do Python, que pode ser traduzido para C e compilado usando o RPython Toolchain. Seu objetivo expresso é ajudar na criação de intérpretes de linguagem, mas também pode ser usado para compilar programas simples.
Para compilar, baixe a fonte atual do PyPy (PyPy 4.0.1) e execute o seguinte:
O executável resultante será nomeado
good-primes-c
ou semelhante no diretório de trabalho atual.Notas de implementação
O gerador de números primos
primes
é uma Peneira de Eratóstenes sem limites, que usa uma roda para evitar múltiplos de 2 , 3 , 5 ou 7 . Ele também se chama recursivamente para gerar o próximo valor a ser usado para marcação. Estou bastante satisfeito com este gerador. A criação de perfil de linha revela que as duas linhas mais lentas são:então eu acho que não há muito espaço para melhorias, além de talvez usar uma roda maior.
Para a verificação da "bondade", primeiro todos os fatores de dois são removidos do n-1 , usando um truque de bits para encontrar a maior potência de dois, que é um divisor
(n-1 & 1-n)
. Como p-1 é necessariamente par para qualquer primo p> 2 , segue-se que 2 deve ser um dos fatores primos distintos. O que resta é enviado para ais_prime_power
função, que faz o que o nome implica. Verificar se um valor é uma potência principal é "quase livre", pois é feito simultaneamente com a verificação de primalidade, com no máximo O (log p n) operações, onde p é o menor fator primo de n. A divisão de teste pode parecer um pouco ingênua, mas, pelos meus testes, é o método mais rápido para valores menores que 2 32 . Economizo um pouco reutilizando a roda da peneira. Em particular:iterando sobre uma roda de comprimento 48, a
p*p < n
verificação será ignorada milhares de vezes, a um preço baixo e baixo de não mais que 48 operações de módulo adicionais. Também pula mais de 77% de todos os candidatos, em vez de 50%, tendo apenas probabilidades.As últimas saídas são:
O código também é válido em Python e deve atingir 3588 ~ 3900 quando executado com um intérprete PyPy recente.
RPython (PyPy 4.0.1), 22596
Esse envio é um pouco diferente dos outros publicados até agora, pois não verifica todos os bons números primos, mas faz saltos relativamente grandes. Uma desvantagem de fazer isso é que as peneiras não podem ser usadas [permaneço corrigida?] ; Portanto, é preciso confiar inteiramente em testes de primalidade que, na prática, são um pouco mais lentos. Há também um meio feliz de ser encontrado entre a taxa de crescimento e o número de valores verificados a cada vez. Valores menores são muito mais rápidos de verificar, mas valores maiores têm maior probabilidade de ter lacunas maiores.
Para apaziguar os deuses da matemática, decidi seguir uma sequência semelhante a Fibonacci, tendo o próximo ponto de partida como a soma das duas anteriores. Se nenhum novo registro for encontrado após a verificação de 10 pares, o script passará para o próximo.
As últimas saídas são:
Quando compilados, números inteiros de 64 bits são usados, embora seja assumido em alguns locais que dois números inteiros possam ser adicionados sem excesso, portanto, na prática, apenas 63 são utilizáveis. Ao atingir 62 bits significativos, o valor atual é reduzido pela metade duas vezes, para evitar transbordamentos no cálculo. O resultado é que o script embaralha os valores no intervalo 2 60 - 2 62 . Não superar a precisão inteira nativa também torna o script mais rápido quando interpretado.
O seguinte script PARI / GP pode ser usado para confirmar este resultado:
fonte
Provavelmente 4032, misturou a peneira de Atkin-Bernstein e "determinista" Miller-Rabin
Fatoração da roda e bons primos
É altamente óbvio que, com as exceções de 2, 3 e 5, todo primo é coprime para 2 * 3 * 5 = 60. Existem 16 classes de equivalência módulo 60 que são coprime para 60, portanto, qualquer teste de primalidade precisa apenas verificar aqueles 16 casos.
No entanto, quando estamos procurando primos "bons", podemos afinar ainda mais o rebanho. Se
gcd(x, 60) = 1
, descobrimos que na maioria dos casosgcd(x-1, 60)
é 6 ou 10. Existem 6 valoresx
para os quais é 2:Portanto, podemos pré-calcular os primos "bons" da forma
2^a 3^b + 1
e2^a 5^b + 1
fundi-los no resultado de um gerador de primos que considera apenas 10% dos números como primos potenciais .Notas de implementação
Como eu já tinha uma implementação em Java da peneira de Atkin-Bernstein e que já usa uma roda como um componente-chave, parecia natural remover os raios desnecessários e adaptar o código. Inicialmente, tentei usar uma arquitetura produtor-consumidor para explorar os 8 núcleos, mas o gerenciamento de memória era muito complicado.
Para testar se um primo é um "bom", estou usando um teste de Miller-Rabin "determinístico" (o que realmente significa um teste de Miller-Rabin que alguém já fez uma verificação prévia de uma lista gerada deterministicamente). Isso certamente pode ser reescrito para usar também o Atkin-Bernstein, com algum cache para cobrir os intervalos correspondentes a sqrt, cbrt etc., mas não tenho certeza se isso seria uma melhoria (porque estaria testando muitos números que Não preciso testar), então isso é um experimento para outro dia.
No meu computador bastante antigo, isso roda para
em quase exatamente 2 minutos e depois
às 3:10, 3:20 e 3:30, respectivamente.
Salve como
PPCG65876.java
, compile comojavac PPCG65876.java
e execute comojava -Xmx1G PPCG65876
.fonte
isGood
verificação.C ++, 2754 (todos os valores, teste de primalidade de força bruta)
Isso é força bruta, mas é um começo antes que nossos matemáticos residentes possam trabalhar com algo mais eficiente.
Posso adicionar mais explicações, se necessário, mas provavelmente é muito óbvio no código. Como if
p
é um primo diferente de 2, sabemos quep - 1
é par e um dos dois fatores é sempre 2. Portanto, enumeramos os primos, reduzimosp - 1
todos os fatores 2 e verificamos se o valor restante é um primo ou se todos os seus fatores são os mesmos primos.Código:
O programa imprime a diferença, bem como os dois bons primos correspondentes, sempre que uma nova diferença máxima é encontrada. Exemplo de saída do teste executado em minha máquina, onde o valor relatado de 2754 é encontrado após cerca de 1:20 minutos:
fonte
C ++, 14226 (somente valores altos, teste de Miller-Rabin)
Postando isso separadamente, porque é totalmente diferente da minha solução inicial, e eu não queria substituir completamente uma postagem que recebeu vários votos positivos.
Agradecemos ao @primo por apontar um problema com a versão original. Houve um excesso de números grandes no teste de números primos.
Isso tira proveito de algumas idéias que foram obtidas durante a evolução de outras soluções. As principais observações são:
Com base nisso, o método empregado aqui é bastante simples:
Resultados:
Código:
fonte
PyPy-2.4.0
Python-2
O
x
arquivoé...Episódio: "Olha mãe! Nem uma única divisão!"
;-)
Eu testei no Debian8 com o PyPy-2.4.0 e o Python2 começou assim:
Se houver realmente muita RAM, a
del L[n]
linha poderá ser excluída.O gerador básico de números primos é este:
Basicamente, faz exatamente o que a peneira de Eratóstenes faz, mas em uma ordem diferente.
L
é um dicionário, mas pode ser visto como lista (fita) de listas de números. Células inexistentesL[n]
são interpretadas comon
até agora não existem divisores primários conhecidos.O
while
loop faz uma decisão primária ou não primária em cada turnoL[n]
.Se
L[n]
existir (igual an in L
),P = L[n]
é uma lista de divisores primos distintos den
. Entãon
não é um primo.Se
L[n]
não existir, nenhum divisor principal foi encontrado. Portanto,n
deve ser primordial,P = [n]
sendo o divisor conhecido.Agora
P
é a lista de divisores primos conhecidos para ambos os casos.O
for p in P
loop move cada entrada deP
avanço pela distância de seu valor na fita de números.É assim que os divisores pulam na fita e é por isso que esses números de salto precisam ser primos. Os novos números só aparecem na fita pela
else
decisão acima e esses são números sem divisores conhecidos além deles mesmos. Os não-primos nunca entram nessas listasL[n]
.Os números primos que saltam na lista são todos distintos porque todos os números
n
são vistos apenas uma vez e apenas são adicionados como divisores (se não for primo :)0
ou (se primo :)1
vezes. Os divisores principais conhecidos apenas avançam, mas nunca são duplicados. Portanto,L[n]
sempre haverá divisores primos distintos ou estarão vazios.De volta ao programa superior para obter as boas lacunas de números primos:
... mantém os divisores primos de
n
emB
quandon
não é conhecida a ser primo.Se
n
for reconhecido como primo,B
mantém a lista de divisores primos do loop pass anterior, olhando paran-1
:... so
len(B) == 2
significan - 1
tem dois divisores primos distintos.g
apenas se lembra do último primo bom visto antes do novo,M
é o comprimento do intervalo máximo de bom primário anterior em
o comprimento do novo intervalo encontrado.Final feliz.
fonte
C #, provavelmente 1932
Descobri que, quanto mais rápido for o seu algoritmo para encontrar números primos, melhor será sua pontuação. Também tenho certeza de que meu algoritmo não é o método mais ideal para a pesquisa principal.
fonte
Python 3, 546
... em dois minutos na minha máquina, o que eu acho que é significativamente menos poderoso que o seu.
Eu provavelmente poderia tornar isso mais eficiente otimizando para o
x=2
caso, mas eh. Bom o bastante. : Pfonte
p: 2, q: 3, |q-p|: 1
para mim.Vá, provavelmente 756
Por vergonha! Sou tão novato que apenas reutilizei ingenuamente algum código antigo e esperava que funcionasse e fosse rápido! Se eu reimplementasse isso e o construísse em torno de bons números primos, seria muito mais rápido, mas, infelizmente, estou aprendendo. (Provavelmente vou responder novamente amanhã com uma solução totalmente reconstruída que foi criada especificamente para você.)
Usa simultaneidade, obviamente.
fonte
Java, 4224 (99,29 s)
Peneira altamente personalizada de eratóstenes com vantagem do BitSet
O tempo gasto depende do limite máximo dos números primos que serão calculados.
Para
fonte
Python 3, 1464
Com a ajuda de Lembik , cuja idéia era apenas verificar os dois primeiros números primos bons depois de um poder de dois e, quando encontrado, passasse imediatamente para o próximo poder de dois. Se alguém puder usar isso como um ponto de partida, fique à vontade. Uma parte dos meus resultados está abaixo depois de executar isso no IDLE. O código segue.
Crédito para primo quando peguei a lista de primos pequenos desse código.
Editar: editei o código para ajustar as especificações reais do problema (dois divisores primos distintos, não exatamente dois divisores primos distintos), e implementei não passar para a próxima potência de dois até que os primos bons encontrados pelo programa tenham um diferença maior que a dos dois últimos primos bons encontrados. Também devo dar crédito a Peter Taylor , pois usei sua ideia de que bons números primos só poderiam ter alguns valores mod 60.
Novamente, eu executei isso em um computador lento no IDLE, portanto os resultados podem ser mais rápidos em algo como PyPy, mas não consegui verificar.
Uma amostra dos meus resultados (p, q, qp, time):
Meu código:
fonte
j
em4
vez de2
? E você parece rejeitar incondicionalmente sej-1
não for um horário nobre um poder de dois, onde você deve testar se é um poder nobre vezes um poder de dois.Ir: Todos os números inteiros: 5112
good.go
:Saída:
Para comparação: peterSO max 5112 em 41.04s versus The Coder max 4176 em 51.97s.
Codificador: max | qp | 4176 q 1964330609 p 1964326433
Saída:
fonte