π ( n ) é o número de primos menor ou igual a n .
Entrada: um número natural, n .
Saída: π (n).
Pontuação: Este é um desafio de código mais rápido . A pontuação será a soma das vezes para os casos de pontuação. Eu cronometrarei cada entrada no meu computador.
Regras e detalhes
Seu código deve funcionar para n até 2 bilhões (2.000.000.000).
Built-ins que trivializam isso não são permitidos. Isso inclui funções π internas ou listas de valores para π ( n ).
Built-ins que testam a primalidade ou geram primos não são permitidos. Isso inclui listas de números primos, que não podem ser consultados externamente ou codificados localmente, exceto com relação ao próximo item.
Você pode codificar números primos de até 19 inclusive, e não superior.
sua implementação de π deve ser determinística. Isso significa que, dado um n específico , seu código deve ser executado (aproximadamente) na mesma quantidade de tempo.
Os idiomas usados devem estar disponíveis gratuitamente no Linux (Centos 7). Instruções devem ser incluídas sobre como executar seu código. Inclua detalhes do compilador / intérprete, se necessário.
Os horários oficiais serão do meu computador.
Ao postar, inclua um tempo medido em alguns / todos os casos de teste / pontuação, apenas para fornecer uma estimativa da velocidade com que seu código está sendo executado.
Os envios devem caber em uma resposta a esta pergunta.
Estou executando o centos7 de 64 bits. Eu tenho apenas 8 GB de RAM e 1 GB de swap. O modelo da CPU é: Processador AMD FX (tm) -6300 de seis núcleos.
Casos de teste ( fonte ):
Input Output
90 24
3000 430
9000 1117
4000000 283146 <--- input = 4*10^6
800000000 41146179 <--- input = 9*10^8
1100000000 55662470 <--- input = 1.1*10^9
Casos de pontuação ( mesma fonte )
Como sempre, esses casos estão sujeitos a alterações. A otimização para os casos de pontuação não é permitida. Também posso alterar o número de casos, em um esforço para equilibrar tempos de execução razoáveis e resultados precisos.
Input Output
1907000000 93875448 <--- input = 1.907*10^9
1337000000 66990613 <--- input = 1.337*10^9
1240000000 62366021 <--- input = 1.24*10^9
660000000 34286170 <--- input = 6.6*10^8
99820000 5751639 <--- input = 9.982*10^7
40550000 2465109 <--- input = 4.055*10^7
24850000 1557132 <--- input = 2.485*10^7
41500 4339
Duração
Como esse é um desafio de código mais rápido e as entradas devem ser executadas no meu computador, reservo-me o direito de interromper as entradas de tempo após 2 semanas. Após esse ponto, as entradas ainda são aceitas, mas não há garantia de que elas tenham sido cronometradas oficialmente.
Dito isto, não espero muitas respostas para este desafio e provavelmente continuarei a cronometrar novas respostas indefinidamente.
Pontuação de Particulares
Programei as entradas mais rápidas com o seguinte script:
#!/bin/bash
a=(1907000000 1337000000 1240000000 660000000 99820000 40550000 24850000 41500)
echo DennisC
exec 2>> times/dennisc.txt
time for j in ${a[@]}; do ./dennisc $j; done >> /dev/null;
echo DennisPy
exec 2>> times/dennispy.txt
time for j in ${a[@]}; do pypy dennispy.py <<< $j; done >> /dev/null;
echo arjandelumens
exec 2>> times/arjandelumens.txt
time for j in ${a[@]}; do ./arjandelumens $j; done >> /dev/null;
echo orlp
exec 2>> times/orlp.txt
time for j in ${a[@]}; do ./orlp $j; done >> /dev/null;
# echo mwr247
# time node-v4.3.1-linux-x64/bin/node mwr247.js
# mwr247 using js seems a bit longer, so I am going to run the fastest
# and then come back to his.
# mwr247 provided a function, so I appended
# console.log( F( <argument> ) )
# to his code, for each argument.
time
escreve para stderr
, então eu enviei stderr
para um arquivo de log usando exec 2 >> <filename>
. Você pode perceber que stdout
é enviado para /dev/null
. Isso não é um problema, porque eu já verifiquei que os programas estavam produzindo a saída correta.
Eu executei o timeall.sh
script acima 10 vezes usandofor i in {1..10}; do ./timeall.sh; done;
Em seguida, calculei a média da real time
pontuação de cada entrada.
Observe que nenhum outro programa estava sendo executado no meu computador durante o tempo.
Além disso, os horários oficiais foram anexados a cada entrada. Por favor, verifique sua própria média.
Respostas:
C, 0,026119s (12 de março de 2016)
Isso usa o método Meissel-Lehmer .
Horários
Na minha máquina, estou recebendo aproximadamente 5,7 milissegundos para os casos de teste combinados. Este é um Intel Core i7-3770 com RAM DDR3 a 1867 MHz, executando o openSUSE 13.2.
Como a variação ficou muito alta , estou usando tempos de dentro do programa para os tempos de execução não oficiais. Este é o script que calculou a média dos tempos de execução combinados.
Horários oficiais
Desta vez, é para fazer os casos de pontuação 1000 vezes.
Como funciona
Fórmula
Seja um número inteiro positivo.x
Cada número inteiro positivo satisfaz exatamente uma das seguintes condições.n≤x
Seja o número de primos tais que . Existem números que se enquadram na quarta categoria.π(y) p p≤y π(x)−π(x−−√3)
Seja denotar a quantidade de números inteiros positivos que são produtos de exatamente números primos que não estão entre os primeiros números primos. Existem números que se enquadram na terceira categoria.Pk(y,c) m≤y k c P2(x,π(x−−√3))
Finalmente, vamos denotar a quantidade de números inteiros positivos que são coprime para os primeiros números primos. Existem números que se enquadram na segunda categoria.ϕ(y,c) k≤y c x−ϕ(x,π(x−−√3))
Como existem números em todas as categorias,x
e, portanto,
Os números na terceira categoria têm uma representação única se exigirmos que e, portanto, . Desta forma, o produto da primos e está na terceira categoria, se e somente se , portanto, não sãop≤q p≤x−−√ p q x−−√3<p≤q≤xp π(xp)−π(p)+1 q p P2(x,π(x−−√3))=∑π(x√3)<k≤π(x√)(π(xpk)−π(pk)+1) pk kth
Algoritmo
Implementação
A seção anterior cobre a maioria das partes do código. Um detalhe restante e importante é como as divisões na função
Phi
são executadas.fastdiv
fonte
C99 / C ++, 8.9208s (28 de fevereiro de 2016)
Uma implementação de peneira de erastotenos baseada em bitmap. Ele executa as seguintes etapas:
Compilado
gcc primecount.c -O3 -lm -Wall
e executado no ubuntu 15.10 (64 bits) em um i7-4970k, leva cerca de 2,2 segundos para o conjunto completo de casos de pontuação. O tempo de execução é dominado pela etapa 3; isso pode ser multithread, se desejado, uma vez que os pedaços são independentes; isso exigiria alguns cuidados para garantir que os limites do pedaço estejam adequadamente alinhados.Aloca um pouco mais de memória do que o estritamente necessário para a peneira; isso abre espaço para alguma saturação no final do buffer, necessária para que o desenrolamento do loop na etapa 3 funcione corretamente.
Horários oficiais
fonte
-O3 -march=native
. Sua CPU suporta apopcnt
instrução e, às vezes, os compiladores podem reconhecer algumas implementações C em puro e compilar com a única instrução. (Ou melhor, use__builtin_popcountll
no GNU C, como a resposta de Dennis).-march=native
na sua CPU Haswell também habilitará o IMC2 para obter instruções mais eficientes de troca de contagem variável. ( SHLX em vez do SHL legado que precisa contarcl
.) A CPU AMD Piledriver do OP não possui BMI2, mas possui popcnt. Porém, os processadores AMD executam SHL de contagem variável mais rápido que os processadores Intel; portanto, a compilação com o BMI2 enquanto o ajuste ainda pode ser apropriado. Piledriver é bastante diferente de Haswell, tanto quanto micro-otimizações ir, mas pedindo-march=native
é bomPython 2 (PyPy 4.0), 2.36961s (29 de fevereiro de 2016)
Isso usa o método Meissel-Lehmer.
Horários
Horários oficiais
Como houve outra resposta em um período semelhante, optei por obter resultados mais precisos. Eu cronometrei isso 100 vezes. A pontuação é o seguinte, dividida por 100.
fonte
Java, 25.725.315 segundos nesta máquina
Isso não vai ganhar , eu só queria postar uma resposta que não use peneiras.
ATUALIZAÇÃO: Atualmente, está classificado em cerca de 150.440.4386 vezes mais lento que a pontuação principal. Suba para votar, a resposta é incrível.
Código de bytes:
Código fonte:
Acontece que o otimizador estava, de fato, aumentando o tempo gasto. > Droga.
A entrada abaixo de 1000 parece levar um tempo médio de 0,157 no meu computador (provavelmente devido ao carregamento da classe ಠ_ಠ), mas após cerca de 1e7, fica complicado.
Lista de tempo:
fonte
javac voteToClose.java
(renomei a classe) e depois o que?java voteToClose <input>
cafe babe
?Ferrugem, 0,37001 seg (12 de junho de 2016)
Cerca de 10 vezes mais lento que a
C
resposta de Dennis , mas 10 vezes mais rápido que sua entrada em Python. Essa resposta é possível por @Shepmaster e @Veedrac, que ajudaram a aprimorá-la na Revisão de Código . É retirado literalmente da publicação de @ Veedrac .Cronometrado com:
time ./time.sh
onde setime.sh
parece:Aqui está a saída.
fonte
Node.js (JavaScript / ES6), 83.549s (11 de novembro de 2016)
Finalmente comecei a refazer isso, e é menor / mais simples e MUITO mais rápido do que antes. Em vez de um método de força bruta mais lento, ele utiliza a Peneira de Eratóstenes juntamente com estruturas de dados mais eficientes, para que agora possa realmente terminar em um tempo respeitável (tanto quanto eu posso encontrar na internet, é a contagem principal de JS mais rápida função lá fora).
Alguns tempos de demonstração (i7-3770k):
fonte
+=1
e não++
?i++
precisa manter a alteração de valor para outra operação, o que nessa escala leva a um pequeno, porém perceptível, desempenho. Não testei o pré-incremento, mas suspeito que será o mesmo que+=1
.+=1
precisa alocar1
na memória. Eu acho que. Se eu fosse você, eu usaria++i
. Eu acho que existe uma única instrução para incrementar um valor, então, não tenho certeza.(...)|0;i=0
ser(...)|(i=0)
C ++ 11, 22.6503s (28 de fevereiro de 2016)
Compile com
g++ -O2 -m64 -march=native -ftree-vectorize -std=c++11 numprimes.cpp
. Essas opções são importantes. Você também precisa ter o Boost instalado. No Ubuntu, isso está disponível instalandolibboost-all-dev
.Se você estiver no Windows, posso recomendar a instalação
g++
e o Boost através do MSYS2 . Eu escrevi um bom tutorial sobre como instalar o MSYS2. Depois de seguir o tutorial, você pode instalar o Boost usandopacman -Sy `pacman -Ssq boost`
.Na minha máquina, isso é executado em 4,8 segundos para 1907000000 (1.9e9).
O código acima foi redirecionado da minha biblioteca pessoal de C ++ , então tive um avanço.
Horários oficiais
fonte
C ++, 2.47215s (29 de fevereiro de 2016)
Esta é uma versão multi-thread (desleixada) da minha outra resposta.
Usa uma peneira segmentada de Eratóstenes com uma fatoração de roda de 6 para pular todos os múltiplos de 2/3. Utiliza o POSIX
ffsll
para ignorar valores compostos consecutivos.Compilar:
horários não oficiais
Cronometrado com um Intel i5-6600k no Ubuntu 15.10, o caso 1907000000 levou
0.817s
.Horários oficiais
Para obter tempos mais precisos, cronometrei isso 100 vezes e depois dividi o tempo por 100.
fonte
C, 2m42.7254s (28 de fevereiro de 2016)
Salvar como
pi.c
, compilar comogcc -o pi pi.c
, executar como./pi <arg>
:Precisa de muita memória para rodar! Se o seu hardware não puder poupar até dois gigabytes de memória real, o programa falhará ou será executado muito lentamente por causa do thrashing do VMM e HD.
O tempo aproximado no meu hardware é de 1.239 × 10 -8 · n 1,065 s. Por exemplo, uma entrada de n = 2 × 10 9 leva cerca de 100 s para ser executada.
Horários oficiais
fonte
if (p==NULL) {exit(1);}
linha ao código, então não acredito que o malloc esteja falhando (também falharia no início, e não 1 minuto). Idéias sobre o que está acontecendo?char
embora eu ache.Julia, 1m 21.1329s
Eu gostaria de apresentar algo um pouco mais rápido, mas por enquanto, aqui está uma implementação bastante ingênua da Peneira de Eratóstenes.
Obtenha a versão mais recente do Julia para o seu sistema aqui . Verifique se o executável Julia está no seu caminho. Salve o código como
sieve.jl
e execute a partir da linha de comando comojulia sieve.jl N
, ondeN
está a entrada.Horários oficiais
fonte
Java, 42.663122s * (3 de março de 2016)
* isso foi programado internamente pelo programa (no computador do OP)
Segue a grande tradição PPCG de código de auto-documentação (embora não no sentido literal: p).
Isso serve para provar que o Java pode ser rápido o suficiente para ser competitivo com outras linguagens de VM ao usar algoritmos semelhantes.
Executar informações
Execute-o como você teria na resposta do @ CoolestVeto, mas o meu não precisa de argumentos de linha de comando, ele pode obtê-los no STDIN.
Ajuste a
NUM_THREADS
constante para configurá-lo como 2x sua contagem de núcleos nativos para obter o desempenho máximo (como observei - no meu caso, tenho 8 núcleos virtuais, portanto, é definido como 16, o OP pode querer 12 para o processador hexa-core).Quando executei esses testes, usei o JDK 1.7.0.45 com o BlueJ 3.1.6 (o IntelliJ estava atualizando) no Windows 10 Enterpise x64 em um laptop ASUS K55VM (Core i7 3610QM, 8GB de RAM). Google Chrome 49.0 de 64 bits com 1 guia (PPCG) aberta e o QBittorrent fazendo o download de 1 arquivo estavam sendo executados em segundo plano, com 60% de uso de RAM no início da execução.
Basicamente,
O programa o guiará pelo resto.
O tempo é feito pelo embutido do Java
System.nanoTime()
.Detalhes do algoritmo:
Possui 3 variantes para diferentes casos de uso - uma versão ingênua como @ CoolestVeto (mas multithread) para entradas abaixo de 2 ^ 15 e uma peneira de Eratóstenes com máscara de bit com eliminação ímpar para entradas acima de 2 ^ 28 e uma peneira normal de Eratóstenes com um Fatoração de 2/3/5/7 de roda para pré-eliminação de múltiplos.
Eu uso a peneira com máscara de bit para evitar argumentos especiais da JVM para os maiores casos de teste. Se isso puder ser feito, a sobrecarga para o cálculo da contagem na versão com máscara de bits pode ser eliminada.
Aqui está a saída:
fonte
Python 3
Usa a peneira de Eratóstenes. É executado em uma média de
8.775s
onden = 10^7
. Até o momento, usei otime
comando embutido . Por exemplo:fonte
AttributeError: 'module' object has no attribute 'maxint'
C ++, 9.3221s (29 de fevereiro de 2016)
Usa uma peneira segmentada de Eratóstenes com uma fatoração de roda de 6 para pular todos os múltiplos de 2/3. Utiliza o POSIX
ffsll
para ignorar valores compostos consecutivos.Pode ser acelerado potencialmente, fazendo a peneira segmentada funcionar em paralelo.
Compilar:
horários não oficiais
Cronometrado com um Intel i5-6600k no Ubuntu 15.10, o caso 1907000000 levou
2.363s
.Horário Oficial
fonte