Calcola il numero di numeri primi fino a n


64

π ( n ) è il numero di numeri primi minori o uguali a n .

Input: un numero naturale, n .

Uscita: π (n).

Punteggio: questa è una sfida con il . Il punteggio sarà la somma dei tempi per i casi di punteggio. Prenderò il tempo di ogni voce sul mio computer.

Regole e dettagli

  • Il codice dovrebbe funzionare per n fino a 2 miliardi (2 miliardi).

  • Non sono ammessi built-in che banalizzano questo. Ciò include funzioni π integrate o elenchi di valori per π ( n ).

  • Non sono consentiti built-in che testano la primalità o generano numeri primi. Ciò include elenchi di numeri primi, che non possono essere cercati esternamente o codificati localmente, tranne per quanto riguarda il prossimo punto elenco.

  • È possibile codificare numeri primi fino a 19 inclusi e non superiori.

  • l'implementazione di π dovrebbe essere deterministica. Ciò significa che, dato un n specifico , il codice dovrebbe essere eseguito (approssimativamente) nello stesso intervallo di tempo.

  • Le lingue utilizzate devono essere disponibili gratuitamente su Linux (Centos 7). Dovrebbero essere incluse le istruzioni su come eseguire il codice. Includi i dettagli del compilatore / interprete se necessario.

  • I tempi ufficiali verranno dal mio computer.

  • Quando pubblichi un post, includi un tempo di auto-misurazione su alcuni / tutti i casi di test / punteggio, solo per darmi una stima della velocità di esecuzione del tuo codice.

  • Le domande devono rientrare in un post di risposta a questa domanda.

  • Sto eseguendo 64 bit centos7. Ho solo 8 GB di RAM e 1 GB di swap. Il modello della cpu è: AMD FX (tm) -6300 processore a sei core.

Casi di prova ( 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

Casi di valutazione ( stessa fonte )

Come al solito, questi casi sono soggetti a modifiche. L'ottimizzazione per i casi di punteggio non è consentita. Posso anche cambiare il numero di casi nel tentativo di bilanciare tempi di esecuzione ragionevoli e risultati accurati.

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

Durata

Poiché si tratta di una sfida con il e le voci devono essere eseguite sul mio computer, mi riservo il diritto di interrompere la registrazione delle voci dopo 2 settimane. Dopo questo punto, le iscrizioni sono ancora accettate, ma non vi è alcuna garanzia che siano ufficialmente programmate.

Detto questo, non mi aspetto troppe risposte a questa sfida e probabilmente continuerò a trovare nuove risposte a tempo indeterminato.

Particolari di punteggio

Ho cronometrato le voci più veloci con il seguente 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.

timescrive stderr, quindi ho inviato stderra un file di registro utilizzando exec 2 >> <filename>. Si può notare che stdoutviene inviato a /dev/null. Questo non è un problema, perché ho già verificato che i programmi stessero producendo l'output corretto.

Ho eseguito lo timeall.shscript sopra 10 volte usandofor i in {1..10}; do ./timeall.sh; done;

Ho quindi calcolato la media del real timepunteggio per ciascuna voce.

Nota che nessun altro programma era in esecuzione sul mio computer durante il cronometraggio.

Inoltre, i tempi ufficiali sono stati aggiunti a ciascuna voce. Per favore ricontrolla la tua media.


Cosa ci impedisce di utilizzare una tabella di ricerca con i primi valori 2e9 di pi (n)? Sarebbe accettabile? (Non sono sicuro di quanto sarebbe veloce, perché sarebbe un grande tavolo)
Luis Mendo,

@DonMuesli Non sarebbe accettabile (va contro lo spirito della sfida), ho modificato per renderlo espressamente proibito anche adesso.
Liam,

8
È pericoloso fare riferimento allo "spirito" della sfida. Il tuo "contro lo spirito" potrebbe essere il "grande trucco" di qualcun altro :-) È meglio che tu l'abbia reso esplicito
Luis Mendo,

1
Che cos'è un built-in? Ho una funzione list-of-primes in una libreria. Posso usarlo? In caso contrario, posso copiare il codice sorgente della libreria nel mio programma e utilizzarlo?
nimi,

1
@Liam: Sì, lo so, ma cosa conta come incorporato? La copia del codice sorgente da una libreria è integrata?
nimi,

Risposte:


119

C, 0.026119s (12 mar 2016)

#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define cache_size 16384
#define Phi_prec_max (47 * a)

#define bit(k) (1ULL << ((k) & 63))
#define word(k) sieve[(k) >> 6]
#define sbit(k) ((word(k >> 1) >> (k >> 1)) & 1)
#define ones(k) (~0ULL >> (64 - (k)))
#define m2(k) ((k + 1) / 2)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define ns(t) (1000000000 * t.tv_sec + t.tv_nsec)
#define popcnt __builtin_popcountll

#define mask_build(i, p, o, m) mask |= m << i, i += o, i -= p * (i >= p)
#define Phi_prec_bytes ((m2(Phi_prec_max) + 1) * sizeof(int16_t))
#define Phi_prec(i, j) Phi_prec_pointer[(j) * (m2(Phi_prec_max) + 1) + (i)]
#define Phi_6_next ((i / 1155) * 480 + Phi_5[i % 1155] - Phi_5[(i + 6) / 13])
#define Phi_6_upd_1() t = Phi_6_next, i += 1, *(l++) = t
#define Phi_6_upd_2() t = Phi_6_next, i += 2, *(l++) = t, *(l++) = t
#define Phi_6_upd_3() t = Phi_6_next, i += 3, *(l++) = t, *(l++) = t, *(l++) = t

typedef unsigned __int128 uint128_t;
struct timespec then, now;
uint64_t a, primes[4648] = { 2, 3, 5, 7, 11, 13, 17, 19 }, *primes_fastdiv;
uint16_t *Phi_6, *Phi_prec_pointer;

inline uint64_t Phi_6_mod(uint64_t y)
{
    if (y < 30030)
        return Phi_6[m2(y)];
    else
        return (y / 30030) * 5760 + Phi_6[m2(y % 30030)];
}

inline uint64_t fastdiv(uint64_t dividend, uint64_t fast_divisor)
{
    return ((uint128_t) dividend * fast_divisor) >> 64;
}

uint64_t Phi(uint64_t y, uint64_t c)
{
    uint64_t *d = primes_fastdiv, i = 0, r = Phi_6_mod(y), t = y / 17;

    r -= Phi_6_mod(t), t = y / 19;

    while (i < c && t > Phi_prec_max) r -= Phi(t, i++), t = fastdiv(y, *(d++));

    while (i < c && t) r -= Phi_prec(m2(t), i++), t = fastdiv(y, *(d++));

    return r;
}

uint64_t Phi_small(uint64_t y, uint64_t c)
{
    if (!c--) return y;

    return Phi_small(y, c) - Phi_small(y / primes[c], c);
}

uint64_t pi_small(uint64_t y)
{
    uint64_t i, r = 0;

    for (i = 0; i < 8; i++) r += (primes[i] <= y);

    for (i = 21; i <= y; i += 2)
        r += i % 3 && i % 5 && i % 7 && i % 11 && i % 13 && i % 17 && i % 19;

    return r;
}

int output(int result)
{
    clock_gettime(CLOCK_REALTIME, &now);
    printf("pi(x) = %9d    real time:%9ld ns\n", result , ns(now) - ns(then));

    return 0;
}

int main(int argc, char *argv[])
{
    uint64_t b, i, j, k, limit, mask, P2, *p, start, t = 8, x = atoi(argv[1]);
    uint64_t root2 = sqrt(x), root3 = pow(x, 1./3), top = x / root3 + 1;
    uint64_t halftop = m2(top), *sieve, sieve_length = (halftop + 63) / 64;
    uint64_t i3 = 1, i5 = 2, i7 = 3, i11 = 5, i13 = 6, i17 = 8, i19 = 9;
    uint16_t Phi_3[] = { 0, 1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8 };
    uint16_t *l, *m, Phi_4[106], Phi_5[1156];

    clock_gettime(CLOCK_REALTIME, &then);

    sieve = malloc(sieve_length * sizeof(int64_t));

    if (x < 529) return output(pi_small(x));

    for (i = 0; i < sieve_length; i++)
    {
        mask  = 0;

        mask_build( i3,  3,  2, 0x9249249249249249ULL);
        mask_build( i5,  5,  1, 0x1084210842108421ULL);
        mask_build( i7,  7,  6, 0x8102040810204081ULL);
        mask_build(i11, 11,  2, 0x0080100200400801ULL);
        mask_build(i13, 13,  1, 0x0010008004002001ULL);
        mask_build(i17, 17,  4, 0x0008000400020001ULL);
        mask_build(i19, 19, 12, 0x0200004000080001ULL);

        sieve[i] = ~mask;
    }

    limit = min(halftop, 8 * cache_size);

    for (i = 21; i < root3; i += 2)
        if (sbit(i))
            for (primes[t++] = i, j = i * i / 2; j < limit; j += i)
                word(j) &= ~bit(j);

    a = t;

    for (i = root3 | 1; i < root2 + 1; i += 2)
        if (sbit(i)) primes[t++] = i;

    b = t;

    while (limit < halftop)
    {
        start = 2 * limit + 1, limit = min(halftop, limit + 8 * cache_size);

        for (p = &primes[8]; p < &primes[a]; p++)
            for (j = max(start / *p | 1, *p) * *p / 2; j < limit; j += *p)
                word(j) &= ~bit(j);
    }

    P2 = (a - b) * (a + b - 1) / 2;

    for (i = m2(root2); b --> a; P2 += t, i = limit)
    {
        limit = m2(x / primes[b]), j = limit & ~63;

        if (i < j)
        {
            t += popcnt((word(i)) >> (i & 63)), i = (i | 63) + 1;

            while (i < j) t += popcnt(word(i)), i += 64;

            if (i < limit) t += popcnt(word(i) & ones(limit - i));
        }
        else if (i < limit) t += popcnt((word(i) >> (i & 63)) & ones(limit - i));
    }

    if (a < 7) return output(Phi_small(x, a) + a - 1 - P2);

    a -= 7, Phi_6 = malloc(a * Phi_prec_bytes + 15016 * sizeof(int16_t));
    Phi_prec_pointer = &Phi_6[15016];

    for (i = 0; i <= 105; i++)
        Phi_4[i] = (i / 15) * 8 + Phi_3[i % 15] - Phi_3[(i + 3) / 7];

    for (i = 0; i <= 1155; i++)
        Phi_5[i] = (i / 105) * 48 + Phi_4[i % 105] - Phi_4[(i + 5) / 11];

    for (i = 1, l = Phi_6, *l++ = 0; i <= 15015; )
    {
        Phi_6_upd_3(); Phi_6_upd_2(); Phi_6_upd_1(); Phi_6_upd_2();
        Phi_6_upd_1(); Phi_6_upd_2(); Phi_6_upd_3(); Phi_6_upd_1();
    }

    for (i = 0; i <= m2(Phi_prec_max); i++)
        Phi_prec(i, 0) = Phi_6[i] - Phi_6[(i + 8) / 17];

    for (j = 1, p = &primes[7]; j < a; j++, p++)
    {
        i = 1, memcpy(&Phi_prec(0, j), &Phi_prec(0, j - 1), Phi_prec_bytes);
        l = &Phi_prec(*p / 2 + 1, j), m = &Phi_prec(m2(Phi_prec_max), j) - *p;

        while (l <= m)
            for (k = 0, t = Phi_prec(i++, j - 1); k < *p; k++) *(l++) -= t;

        t = Phi_prec(i++, j - 1);

        while (l <= m + *p) *(l++) -= t;
    }

    primes_fastdiv = malloc(a * sizeof(int64_t));

    for (i = 0, p = &primes[8]; i < a; i++, p++)
    {
        t = 96 - __builtin_clzll(*p);
        primes_fastdiv[i] = (bit(t) / *p + 1) << (64 - t);
    }

    return output(Phi(x, a) + a + 6 - P2);
}

Questo utilizza il metodo Meissel-Lehmer .

Tempi

Sulla mia macchina, sto ottenendo circa 5,7 millisecondi per i casi di test combinati. Questo è su un Intel Core i7-3770 con DDR3 RAM a 1867 MHz, con openSUSE 13.2.

$ ./timepi '-march=native -O3' pi 1000
pi(x) =  93875448    real time:  2774958 ns
pi(x) =  66990613    real time:  2158491 ns
pi(x) =  62366021    real time:  2023441 ns
pi(x) =  34286170    real time:  1233158 ns
pi(x) =   5751639    real time:   384284 ns
pi(x) =   2465109    real time:   239783 ns
pi(x) =   1557132    real time:   196248 ns
pi(x) =      4339    real time:    60597 ns

0.00572879 s

Poiché la varianza è diventata troppo elevata , sto usando i tempi all'interno del programma per i tempi di esecuzione non ufficiali. Questo è lo script che ha calcolato la media dei tempi di esecuzione combinati.

#!/bin/bash

all() { for j in ${a[@]}; do ./$1 $j; done; }

gcc -Wall $1 -lm -o $2 $2.c

a=(1907000000 1337000000 1240000000 660000000 99820000 40550000 24850000 41500)

all $2

r=$(seq 1 $3)

for i in $r; do all $2; done > times

awk -v it=$3 '{ sum += $6 } END { print "\n" sum / (1e9 * it) " s" }' times

rm times

Tempi ufficiali

Questa volta è per fare i casi punteggio 1000 volte.

real    0m28.006s
user    0m15.703s
sys 0m14.319s

Come funziona

Formula

Sia un numero intero positivo.X

Ogni intero positivo soddisfa esattamente una delle seguenti condizioni.nX

  1. n=1

  2. p [ 1 , 3 n è divisibile per un numero primo in .p[1,X3]

  3. p q ( 3 n=pq , dove e sono numeri primi (non necessariamente distinti) in .pq(X3,X23)

  4. n > 3 n è prime en>X3

Let denota il numero di numeri primi tali che . Ci sono numeri che rientrano nella quarta categoria.p p y π ( x ) - π ( 3 π(y)ppyπ(X)-π(X3)

Sia la quantità di numeri interi positivi che sono un prodotto di esattamente numeri primi non tra i primi numeri primi. Esistono numeri che rientrano nella terza categoria.m y k c P 2 ( x , π ( 3 PK(y,c)myKcP2(X,π(X3))

Infine, lascia che denoti la quantità di numeri interi positivi che sono coprimi ai primi numeri primi . Esistono numeri che rientrano nella seconda categoria.k y c x - ϕ ( x , π ( 3 φ(y,c)KycX-φ(X,π(X3))

Poiché ci sono numeri in tutte le categorie,X

1+X-φ(X,π(X3))+P2(X,π(X3))+π(X)-π(X3)=X

e quindi,

π(X)=φ(X,π(X3))+π(X3)-1-P2(X,π(X3))

I numeri nella terza categoria hanno una rappresentazione univoca se si richiede che e, quindi, . In questo modo, il prodotto dei numeri primi e è nella terza categoria se e solo se , quindi ci sono possibili valori per per un valore fisso di e , dove indica il numero primo .p pq pq 3 pXpq π(xX3<pqXpq p P 2 ( x , π ( 3 π(Xp)-π(p)+1qpp k k thP2(X,π(X3))=Σπ(X3)<Kπ(X)(π(XpK)-π(pK)+1)pKKesimo

Infine, ogni numero intero positivo che non è coprimi ai primi numeri primi può essere espresso in modo univoco come , dove è il fattore primo più basso di . In questo modo, ed sono coprimi ai primi numeri primi .c n = p k f p knycn=pKfpKk c f k - 1nKcfK-1

Questo porta alla formula ricorsiva . In particolare, la somma è vuota se , quindi .c = 0 ϕ ( y , 0 ) = yφ(y,c)=y-Σ1Kcφ(ypK,K-1)c=0φ(y,0)=y

Ora abbiamo una formula che ci consente di calcolare generando solo i primi numeri primi (milioni vs miliardi).π ( 3 π(X)π(X23)

Algoritmo

Dovremo calcolare , dove può arrivare a un minimo di . Mentre ci sono altri modi per farlo (come applicare la nostra formula in modo ricorsivo), il modo più veloce sembra essere quello di enumerare tutti i numeri primi fino a , che può essere fatto con il setaccio di Eratostene.p3π(Xp)p 3X3X23

Innanzitutto, identifichiamo e memorizziamo tutti i numeri primi in e calcoliamo e contemporaneamente. Quindi, calcoliamo per tutti i in e contiamo i numeri primi fino a ogni quoziente successivo .π( 3 [1,X]π(π(X3) xπ(X)XpK( π ( 3 K(π(X3),π(X)]

Inoltre, ha la forma chiusa , che ci permette di completare il calcolo di .Σπ(X3)<Kπ(X)(-π(pK)+1) P2(xπ(X3)-π(X))(π(X3)+π(X)-12P2(X,π(X3))

Ciò lascia il calcolo di , che è la parte più costosa dell'algoritmo. Il semplice utilizzo della formula ricorsiva richiederebbe chiamate di funzione per calcolare .2 c ϕ (φ2cφ(y,c)

Prima di tutto, per tutti i valori di , quindi . Di per sé, questa osservazione è già sufficiente per rendere fattibile il calcolo. Questo perché qualsiasi numero inferiore a è più piccolo del prodotto di dieci numeri primi distinti, quindi la stragrande maggioranza delle somme svanisce.c ϕ ( y , c ) = y - 1 k c , p ky ϕ ( yφ(0,c)=0c2109ϕ(y,c)=y1kc,pkyϕ(ypk,k1)2109

Inoltre, raggruppando e le prime sintesi della definizione di , otteniamo la formula alternativa . Pertanto, pre-calcolare per una fissa e i valori appropriati di salva la maggior parte delle chiamate di funzione rimanenti e i calcoli associati.c ϕ ϕ ( y , c ) = ϕ ( y , c ) - c < k c , p ky ϕ ( yyc'φϕ(y,c)cϕ(y,c)=ϕ(y,c)c<kc,pkyϕ(ypk,k1)ϕ(y,c)c'y

Se , allora , poiché gli interi in che sono divisibili per nessuno di sono precisamente quelli che sono coprime di . Inoltre, poiché , abbiamo che . ϕ ( m c , c )mc=1kcpk[ 1 , m c ] p 1 , , p c m c gcd ( z + m c , m c ) = mcd ( z , m c ) = ϕϕ(mc,c)=φ(mc)[1,mc]p1,,pcmcϕ ( y ,gcd(z+mc,mc)=gcd(z,mc)ϕ(y,c)=ϕ(ymcmc,c)+φ(y

Dato che la funzione di totaggio di Eulero è moltiplicativa, , e abbiamo un modo semplice per derivare per tutti precompilando i valori solo per quelli in .φ(mc)=Π1Kcφ(pK)=Π1Kc(pK-1)y y [ 0 , m c )φ(y,c)yy[0,mc)

Inoltre, se impostiamo , otteniamo , il definizione originale dall'articolo di Lehmer. Questo ci dà un modo semplice per pre-calcolare per aumentare i valori di .ϕ ( y , c ) = ϕ ( y , c - 1 )c=c1ϕ(y,cϕ(y,c)=ϕ(y,c1)ϕ(ypc,c1)cϕ(y,c)c

Inoltre per il pre-calcolo per un certo valore basso di , lo pre-calcoleremo anche per valori bassi di , tagliando la ricorsione poco dopo essere scesa sotto una certa soglia.cϕ(y,c)cy

Implementazione

La sezione precedente copre la maggior parte delle parti del codice. Un dettaglio rimanente e importante è come Phivengono eseguite le divisioni nella funzione .

Poiché computing richiede solo la divisione per i primi numeri primi , possiamo invece usare la funzione. Invece di dividere semplicemente una per una primaria , moltiplichiamo per e recuperiamo come . A causa di come viene implementata la moltiplicazione dei numeri interi su x64 , non è richiesta la divisione per ; i 64 bit più alti di sono memorizzati nel proprio registro.ϕypydpπ(x3)fastdivypydp264pyp 264dpy264264dpy

Si noti che questo metodo richiede il pre calcolo di , che non è più veloce del computing direttamente. Tuttavia, poiché dobbiamo dividere gli stessi numeri primi più e più volte e la divisione è molto più lenta della moltiplicazione, ciò si traduce in un importante accelerazione. Maggiori dettagli su questo algoritmo, nonché una dimostrazione formale, possono essere trovati in Division da Invariant Integers usando la moltiplicazione .dpyp


22
Uno non supera semplicemente Dennis?
Addison Crump,

8
Onestamente non riesco proprio a credere quanto sia veloce. Non ho avuto il tempo di passare e capire cosa sta succedendo, ma ne ho davvero bisogno.
Liam,

27
@Liam ho intenzione di spiegare come funziona, ma sto ancora cercando di accelerarlo. In questo momento, vorrei davvero che PPCG avesse LaTeX ...
Dennis il

15
Nota divertente: (Sulla mia macchina) Questo sta battendo sia il builtin di Mathematica che il kimwalisch sulla libreria C ++ del primecount di github, tuttavia, attualmente è l'unica voce che lo fa.
Michael Klein

10
@TheNumberOne non dirglielo ... altre persone potrebbero averne bisogno per batterlo
Liam

24

C99 / C ++, 8.9208s (28 feb 2016)

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

uint64_t popcount( uint64_t v )
    {
    v = (v & 0x5555555555555555ULL) + ((v>>1) & 0x5555555555555555ULL);
    v = (v & 0x3333333333333333ULL) + ((v>>2) & 0x3333333333333333ULL);
    v = (v & 0x0F0F0F0F0F0F0F0FULL) + ((v>>4) & 0x0F0F0F0F0F0F0F0FULL);
    v *= 0x0101010101010101ULL;
    return v >> 56;
    }

#define PPROD  3*5*7

int primecount( int limit )
    {
    int i,j;
    int reps = (limit-1)/(64*PPROD) + 1;
    int mod_limit = reps * (64*PPROD);
    int seek_limit = (int)ceil( sqrt(limit) );
    int primecount = 0;
    int slice_count = limit/250000 + 1;

    uint8_t *buf = (uint8_t *)malloc( mod_limit/8 + seek_limit);
    int *primes = (int *)malloc(seek_limit*sizeof(int));

    // initialize a repeating bit-pattern to fill our sieve-memory with
    uint64_t v[PPROD];
    memset(v, 0, sizeof(v) );
    for(i=0;i<(64*PPROD);i++)
        for(j=2;j<=7;j++)
            if( i % j == 0 )
                v[ i >> 6 ] |= 1ULL << (i & 0x3F);

    for(i=0; i<reps; i++)
        memcpy( buf + 8*PPROD*i, v, 8*PPROD );

    // use naive E-sieve to get hold of all primes to test for
    for(i=11;i<seek_limit;i+=2)
        {
        if( (buf[i >> 3] & (1 << (i & 7)) ) == 0 )
            {
            primes[primecount++] = i;
            for(j=3*i;j<seek_limit;j += 2*i )
                buf[j >> 3] |= (1 << (j&7) );
            }
        }

    // fill up whole E-sieve. Use chunks of about 30 Kbytes
    // so that the chunk of E-sieve we're working on
    // can fit into the L1-cache.
    for(j=0;j<slice_count;j++)
        {
        int low_bound = ((uint64_t)limit * j) / slice_count;
        int high_bound = ((uint64_t)limit * (j+1)) / slice_count - 1;

        for(i=0;i<primecount;i++)
            {
            int pm = primes[i];
            // compute the first odd multiple of pm that is larger than or equal
            // to the lower bound.
            uint32_t lb2 = (low_bound + pm - 1) / pm;
            lb2 |= 1;
            if( lb2 < 3 ) lb2 = 3;
            lb2 *= pm;
            uint32_t hb2 = (high_bound / pm) * pm;

            uint32_t kt1 = ((lb2 + 2*pm) >> 3) - (lb2 >> 3);
            uint32_t kt2 = ((lb2 + 4*pm) >> 3) - (lb2 >> 3);
            uint32_t kt3 = ((lb2 + 6*pm) >> 3) - (lb2 >> 3);

            uint32_t kx0 = 1 << (lb2 & 7);
            uint32_t kx1 = 1 << ((lb2 + 2*pm) & 7);
            uint32_t kx2 = 1 << ((lb2 + 4*pm) & 7);
            uint32_t kx3 = 1 << ((lb2 + 6*pm) & 7);

            uint8_t *lb3 = buf + (lb2 >> 3);
            uint8_t *hb3 = buf + (hb2 >> 3);

            uint8_t *kp;
            for(kp=lb3; kp<=hb3; kp+=pm)
                {
                kp[0]   |= kx0;
                kp[kt1] |= kx1;
                kp[kt2] |= kx2;
                kp[kt3] |= kx3;
                }
            }
        }

    // flag tail elements to exclude them from prime-counting.
    for(i=limit;i<mod_limit;i++)
        buf[i >> 3] |= 1 << (i&7);

    int sum = 0;
    uint64_t *bufx = (uint64_t *)buf;

    for(i=0;i<mod_limit>>6;i++)
        sum += popcount( bufx[i] );

    free(buf);
    free(primes);

    return mod_limit - sum + 3;
    }


int main( int argc, char **argv)
    {
    if( argc != 2 )
        {
        printf("Please provide an argument\n");
        exit(1);
        }

    int limit = atoi( argv[1] );
    if( limit < 3 || limit > 2000000000 )
        {
        printf("Argument %d out of range\n", limit );
        exit(1);
        }

    printf("%d\n", primecount(limit) );
    }

Un'implementazione setaccio di erastothen basata su bitmap. Esegue i seguenti passaggi:

  1. Innanzitutto, genera uno schema di bit ripetuto per riempire il setaccio, che copre multipli di 2,3,5,7
  2. Quindi, utilizzare il metodo sieve per generare un array di tutti i numeri primi più piccolo di sqrt (n)
  3. Quindi, utilizzare l'elenco principale del passaggio precedente per scrivere nel setaccio. Questo viene fatto su blocchi del setaccio che sono approssimativamente delle dimensioni della cache L1, in modo che l'elaborazione del setaccio non blocchi costantemente la cache L1; questo sembra comportare un aumento di velocità 5 volte rispetto al non blocco.
  4. Infine, esegui un conteggio dei bit.

Compilato gcc primecount.c -O3 -lm -Walled eseguito su Ubuntu 15.10 (64 bit) su un i7-4970k, sono necessari circa 2,2 secondi per l'intero set di casi di punteggio. Il tempo di esecuzione è dominato dal passaggio 3; questo può essere multithread, se desiderato, poiché i blocchi sono indipendenti; questo richiederebbe un po 'di attenzione per garantire che i confini del blocco siano opportunamente allineati.

Alloca leggermente più memoria di quanto strettamente necessario per il setaccio; questo fa spazio per un certo sovraccarico di fine buffer, necessario per il corretto svolgimento del loop nel passaggio 3.

Tempi ufficiali

real    0m8.934s
user    0m8.795s
sys 0m0.150s

real    0m8.956s
user    0m8.818s
sys 0m0.150s

real    0m8.907s
user    0m8.775s
sys 0m0.145s

real    0m8.904s
user    0m8.775s
sys 0m0.141s

real    0m8.902s
user    0m8.783s
sys 0m0.132s

real    0m9.087s
user    0m8.923s
sys 0m0.176s

real    0m8.905s
user    0m8.778s
sys 0m0.140s

real    0m9.005s
user    0m8.859s
sys 0m0.158s

real    0m8.911s
user    0m8.789s
sys 0m0.135s

real    0m8.907s
user    0m8.781s
sys 0m0.138s

8
Benvenuti in Programming Puzzles & Code Golf e congratulazioni per un primo post stellare !
Dennis,

Prendi in considerazione l'utilizzo -O3 -march=native. La tua CPU supporta l' popcntistruzione e talvolta i compilatori possono riconoscerne alcune implementazioni in puro C e compilarla per la singola istruzione. (O meglio, usa __builtin_popcountllsu GNU C, come la risposta di Dennis).
Peter Cordes,

-march=nativesulla tua CPU Haswell abiliterà anche l'IMC2 per istruzioni di spostamento conteggio delle variabili più efficienti. ( SHLX invece del SHL legacy che deve essere conteggiato cl.) La CPU Piledriver AMD dell'OP non ha BMI2, ma ha popcnt. Ma le CPU AMD eseguono SHL a conteggio variabile più velocemente delle CPU Intel, quindi la compilazione con BMI2 durante l'ottimizzazione potrebbe essere ancora appropriata. Piledriver è abbastanza diverso da Haswell per quanto riguarda le micro-ottimizzazioni, ma chiedere -march=nativeè buono
Peter Cordes,

12

Python 2 (PyPy 4.0), 2.36961s (29 febbraio 2016)

def Phi(m, b):
    if not b:
        return m
    if not m:
        return 0
    if m >= 800:
        return Phi(m, b - 1) - Phi(m // primes[b - 1], b - 1)
    t = b * 800 + m
    if not Phi_memo[t]:
        Phi_memo[t] =  Phi(m, b - 1) - Phi(m // primes[b - 1], b - 1)
    return Phi_memo[t]

x = int(input())

if x < 6:
    print [0, 0, 1, 2, 2, 3][x]
    exit()

root2 = int(x ** (1./2))
root3 = int(x ** (1./3))
top = x // root3 + 1
sieve = [0, 0] + [1] * (top - 2)
pi = [0, 0]
primes = []
t = 0

for i in range(2, top):
    if sieve[i] == 1:
        t += 1
        primes.append(i)
        sieve[i::i] = [0] * len(sieve[i::i])
    pi.append(t)

a, b = pi[root3 + 1], pi[root2 + 1]
Phi_memo = [0] * ((a + 1) * 800)

print Phi(x, a) + a - 1 - sum(pi[x // p] - pi[p] + 1 for p in primes[a:b])

Questo utilizza il metodo Meissel-Lehmer.

Tempi

$ time for i in 1.907e9 1.337e9 1.24e9 6.6e8 9.982e7 4.055e7 2.485e7 41500
> do pypy pi.py <<< $i; done
93875448
66990613
62366021
34286170
5751639
2465109
1557132
4339

real    0m1.696s
user    0m1.360s
sys     0m0.332s

Tempi ufficiali

Dato che c'era un'altra risposta con un tempo simile, ho optato per ottenere risultati più precisi. L'ho cronometrato 100 volte. Il punteggio è il seguente tempo diviso per 100.

real    3m56.961s
user    3m38.802s
sys 0m18.512s

5
Inoltre, solo per nota: questo codice è 15.102,4 volte più veloce del mio. +1
Addison Crump,

12

Java, 25.725.315 secondi su questa macchina

Questo non vincerà , volevo solo pubblicare una risposta che non utilizza alcun setaccio.

AGGIORNAMENTO: attualmente è classificato a circa 150.440.4386 volte più lento del punteggio principale. Votali, vota, la loro risposta è fantastica.

Codice byte:

0000000: cafe babe 0000 0034 0030 0a00 0900 1709  .......4.0......
0000010: 0018 0019 0a00 1a00 1b0a 0008 001c 0a00  ................
0000020: 1d00 1e0a 0008 001f 0a00 2000 2107 0022  .......... .!.."
0000030: 0700 2301 0006 3c69 6e69 743e 0100 0328  ..#...<init>...(
0000040: 2956 0100 0443 6f64 6501 000f 4c69 6e65  )V...Code...Line
0000050: 4e75 6d62 6572 5461 626c 6501 0004 6d61  NumberTable...ma
0000060: 696e 0100 1628 5b4c 6a61 7661 2f6c 616e  in...([Ljava/lan
0000070: 672f 5374 7269 6e67 3b29 5601 0008 6e75  g/String;)V...nu
0000080: 6d50 7269 6d65 0100 0428 4929 4901 000d  mPrime...(I)I...
0000090: 5374 6163 6b4d 6170 5461 626c 6501 0007  StackMapTable...
00000a0: 6973 5072 696d 6501 0004 2849 295a 0100  isPrime...(I)Z..
00000b0: 0a53 6f75 7263 6546 696c 6501 0006 452e  .SourceFile...E.
00000c0: 6a61 7661 0c00 0a00 0b07 0024 0c00 2500  java.......$..%.
00000d0: 2607 0027 0c00 2800 290c 0010 0011 0700  &..'..(.).......
00000e0: 2a0c 002b 002c 0c00 1300 1407 002d 0c00  *..+.,.......-..
00000f0: 2e00 2f01 0001 4501 0010 6a61 7661 2f6c  ../...E...java/l
0000100: 616e 672f 4f62 6a65 6374 0100 106a 6176  ang/Object...jav
0000110: 612f 6c61 6e67 2f53 7973 7465 6d01 0003  a/lang/System...
0000120: 6f75 7401 0015 4c6a 6176 612f 696f 2f50  out...Ljava/io/P
0000130: 7269 6e74 5374 7265 616d 3b01 0011 6a61  rintStream;...ja
0000140: 7661 2f6c 616e 672f 496e 7465 6765 7201  va/lang/Integer.
0000150: 0008 7061 7273 6549 6e74 0100 1528 4c6a  ..parseInt...(Lj
0000160: 6176 612f 6c61 6e67 2f53 7472 696e 673b  ava/lang/String;
0000170: 2949 0100 136a 6176 612f 696f 2f50 7269  )I...java/io/Pri
0000180: 6e74 5374 7265 616d 0100 0770 7269 6e74  ntStream...print
0000190: 6c6e 0100 0428 4929 5601 000e 6a61 7661  ln...(I)V...java
00001a0: 2f6c 616e 672f 4d61 7468 0100 0473 7172  /lang/Math...sqr
00001b0: 7401 0004 2844 2944 0021 0008 0009 0000  t...(D)D.!......
00001c0: 0000 0004 0001 000a 000b 0001 000c 0000  ................
00001d0: 001d 0001 0001 0000 0005 2ab7 0001 b100  ..........*.....
00001e0: 0000 0100 0d00 0000 0600 0100 0000 0100  ................
00001f0: 0900 0e00 0f00 0100 0c00 0000 2c00 0300  ............,...
0000200: 0100 0000 10b2 0002 2a03 32b8 0003 b800  ........*.2.....
0000210: 04b6 0005 b100 0000 0100 0d00 0000 0a00  ................
0000220: 0200 0000 0300 0f00 0400 0a00 1000 1100  ................
0000230: 0100 0c00 0000 6600 0200 0300 0000 2003  ......f....... .
0000240: 3c03 3d1c 1aa2 0018 1b1c b800 0699 0007  <.=.............
0000250: 04a7 0004 0360 3c84 0201 a7ff e91b ac00  .....`<.........
0000260: 0000 0200 0d00 0000 1600 0500 0000 0600  ................
0000270: 0200 0700 0900 0800 1800 0700 1e00 0900  ................
0000280: 1200 0000 1800 04fd 0004 0101 5001 ff00  ............P...
0000290: 0000 0301 0101 0002 0101 fa00 0700 0a00  ................
00002a0: 1300 1400 0100 0c00 0000 9700 0300 0300  ................
00002b0: 0000 4c1a 05a2 0005 03ac 1a05 9f00 081a  ..L.............
00002c0: 06a0 0005 04ac 1a05 7099 0009 1a06 709a  ........p.....p.
00002d0: 0005 03ac 1a87 b800 078e 0460 3c10 063d  ...........`<..=
00002e0: 1c1b a300 1b1a 1c04 6470 9900 0b1a 1c04  ........dp......
00002f0: 6070 9a00 0503 ac84 0206 a7ff e604 ac00  `p..............
0000300: 0000 0200 0d00 0000 2200 0800 0000 0c00  ........".......
0000310: 0700 0d00 1300 0e00 2100 0f00 2a00 1000  ........!...*...
0000320: 3200 1100 4400 1000 4a00 1200 1200 0000  2...D...J.......
0000330: 1100 0907 0901 0b01 fd00 0b01 0114 01fa  ................
0000340: 0005 0001 0015 0000 0002 0016            ............

Codice sorgente:

public class E {
    public static void main(String[]args){
        System.out.println(numPrime(Integer.parseInt(args[0])));
    }
    private static int numPrime(int max) {
        int toReturn = 0;
        for (int i = 0; i < max; i++)
            toReturn += (isPrime(i))?1:0;
        return toReturn;
    }
    private static boolean isPrime(int n) {
            if(n < 2) return false;
            if(n == 2 || n == 3) return true;
            if(n%2 == 0 || n%3 == 0) return false;
            int sqrtN = (int)Math.sqrt(n)+1;
            for(int i = 6; i <= sqrtN; i += 6)
                if(n%(i-1) == 0 || n%(i+1) == 0) return false;
            return true;
    }
}

Si scopre che l'ottimizzatore stava, in effetti, aumentando il tempo impiegato. >.> Dannazione.

L'input inferiore a 1000 sembra impiegare un tempo medio di .157s sul mio computer (probabilmente a causa del caricamento della classe ಠ_ಠ), ma dopo circa 1e7 diventa esiguo.

Elenco dei tempi:

> time java E 41500;time java E 24850000;time java E 40550000;time java E 99820000;time java E 660000000;time java E 1240000000;time java E 1337000000;time java E 1907000000
4339

real    0m0.236s
user    0m0.112s
sys     0m0.024s
1557132

real    0m8.842s
user    0m8.784s
sys     0m0.060s
2465109

real    0m18.442s
user    0m18.348s
sys     0m0.116s
5751639

real    1m15.642s
user    1m8.772s
sys     0m0.252s
34286170

real    40m35.810s
user    16m5.240s
sys     0m5.820s
62366021

real    104m12.628s
user    39m32.348s
sys     0m13.584s
66990613

real    110m22.064s
user    42m28.092s
sys     0m11.320s
93875448

real    171m51.650s
user    68m39.968s
sys     0m14.916s

11
Java è attualmente in esecuzione con una CPU coerente al 100% in questo momento. Questo è totalmente efficiente, di cosa stai parlando?
Addison Crump,

puoi darmi un tutorial per uscire su come java (perché C / C ++> java). Compilo con javac voteToClose.java(ho ribattezzato la classe) e poi cosa?
Liam,

@Liamjava voteToClose <input>
Addison Crump,

1
Aspetta ... Perché dice il codice byte cafe babe?
Cyoce,

12
@Cyoce Tutti i file di classe Java sono intestati a 0xCAFEBABE.
Addison Crump,

8

Rust, 0.37001 sec (12 giugno 2016)

Circa 10 volte più lento che lento della Crisposta di Dennis , ma 10 volte più veloce della sua voce in Python. Questa risposta è resa possibile da @Shepmaster e @Veedrac, che hanno contribuito a migliorarlo in Code Review . È tratto letteralmente dal post di @ Veedrac .

use std::env;

const EMPTY: usize = std::usize::MAX;
const MAX_X: usize = 800;

fn main() {
    let args: Vec<_> = env::args().collect();
    let x: usize = args[1].trim().parse().expect("expected a number");

    let root = (x as f64).sqrt() as usize;
    let y = (x as f64).powf(0.3333333333333) as usize + 1;

    let sieve_size = x / y + 2;
    let mut sieve = vec![true; sieve_size];
    let mut primes = vec![0; sieve_size];
    sieve[0] = false;
    sieve[1] = false;

    let mut a = 0;
    let mut num_primes = 1;

    let mut num_primes_smaller_root = 0;

    // find all primes up to x/y ~ x^2/3 aka sieve_size
    for i in 2..sieve_size {
        if sieve[i] {
            if i <= root {
                if i <= y {
                    a += 1;
                }
                num_primes_smaller_root += 1;
            }

            primes[num_primes] = i;
            num_primes += 1;
            let mut multiples = i;
            while multiples < sieve_size {
                sieve[multiples] = false;
                multiples += i;
            }
        }
    }

    let interesting_primes = primes[a + 1..num_primes_smaller_root + 1].iter();

    let p_2 =
        interesting_primes
        .map(|ip| primes.iter().take_while(|&&p| p <= x / ip).count())
        .enumerate()
        .map(|(i, v)| v - 1 - i - a)
        .fold(0, |acc, v| acc + v);

    let mut phi_results = vec![EMPTY; (a + 1) * MAX_X];
    println!("pi({}) = {}", x, phi(x, a, &primes, &mut phi_results) + a - 1 - p_2);
}

fn phi(x: usize, b: usize, primes: &[usize], phi_results: &mut [usize]) -> usize {
    if b == 0 {
        return x;
    }

    if x < MAX_X && phi_results[x + b * MAX_X] != EMPTY {
        return phi_results[x + b * MAX_X];
    }

    let value = phi(x, b - 1, primes, phi_results) - phi(x / primes[b], b - 1, primes, phi_results);
    if x < MAX_X {
        phi_results[x + b * MAX_X] = value;
    }
    value
}

Cronometrato con: time ./time.shdove time.shappare:

#!/bin/bash

a=(1907000000 1337000000 1240000000 660000000 99820000 40550000 24850000 41500)

for i in {0..100}; do
    for j in ${a[@]}; do
        ./target/release/pi_n $j  > /dev/null;
    done;
done;

Ecco l'output.

[me@localhost pi_n]$ time ./time.sh 

real    0m37.011s
user    0m34.752s
sys 0m2.410s

8

Node.js (JavaScript / ES6), 83.549s (11 nov 2016)

var n=process.argv[2]*1,r=new Uint8Array(n),p=0,i=1,j
while(++i<=n){
  if(r[i]===0){
    for(j=i*i;j<=n;j+=i){r[j]=1}
    p+=1
  }
}
console.log(p)

Finalmente sono riuscito a rifare questo, ed è sia più piccolo / più semplice che MOLTO più veloce di prima. Piuttosto che un metodo di forza bruta più lento, utilizza il setaccio di Eratostene insieme a strutture di dati più efficienti, in modo che ora sia in grado di finire effettivamente in un tempo rispettabile (per quanto posso trovare su Internet, è il conteggio dei primi JS più veloce funzione là fuori).

Alcuni tempi demo (i7-3770k):

10^4 (10,000) => 0.001 seconds
10^5 (100,000) => 0.003 seconds
10^6 (1,000,000) => 0.009 seconds
10^7 (10,000,000) => 0.074 seconds
10^8 (100,000,000) => 1.193 seconds
10^9 (1,000,000,000) => 14.415 seconds

Perché +=1e no ++?
ETHproductions

@ETHproductions Dipende se si intende pre o post-incremento. i++deve contenere la modifica del valore per un'altra operazione, che su questa scala porta a un piccolo ma notevole successo delle prestazioni. Non ho testato il pre-incremento, ma sospetto che sarà più o meno lo stesso di +=1.
Mwr247,

Ma ha +=1bisogno di allocare 1in memoria. Penso. Se fossi in te, lo userei ++i. Penso che ci sia una singola istruzione per incrementare un valore, quindi non ne sono sicuro.
Ismael Miguel,

Perché è così condensato? Non si tratta di code-golf ed è davvero difficile da leggere.
Cyoce,

Inoltre, potrebbe essere utile passare (...)|0;i=0a(...)|(i=0)
Cyoce,

6

C ++ 11, 22.6503s (28 feb 2016)

Compila con g++ -O2 -m64 -march=native -ftree-vectorize -std=c++11 numprimes.cpp. Queste opzioni sono importanti. È inoltre necessario disporre di Boost installato. Su Ubuntu questo è disponibile installando libboost-all-dev.

Se sei su Windows, posso consigliare di installare g++e potenziare MSYS2 . Ho scritto un bel tutorial su come installare MSYS2. Dopo aver seguito il tutorial è possibile installare Boost usando pacman -Sy `pacman -Ssq boost`.

#include <cmath>
#include <cstdint>
#include <iostream>
#include <vector>
#include <boost/dynamic_bitset.hpp>

uint64_t num_primes(uint64_t n) {
    // http://stackoverflow.com/questions/4643647/fast-prime-factorization-module
    uint64_t pi = (n >= 2) + (n >= 3);
    if (n < 5) return pi;

    n += 1;
    uint64_t correction = n % 6 > 1;
    uint64_t wheels[6] = { n, n - 1, n + 4, n + 3, n + 2, n + 1 };
    uint64_t limit = wheels[n % 6];

    boost::dynamic_bitset<> sieve(limit / 3);
    sieve.set();
    sieve[0] = false;

    for (uint64_t i = 0, upper = uint64_t(std::sqrt(limit))/3; i <= upper; ++i) {
        if (sieve[i]) {
            uint64_t k = (3*i + 1) | 1;
            for (uint64_t j = (k*k) / 3;                   j < limit/3; j += 2*k) sieve[j] = false;
            for (uint64_t j = (k*k + 4*k - 2*k*(i & 1))/3; j < limit/3; j += 2*k) sieve[j] = false;
        }
    }

    pi += sieve.count();
    for (uint64_t i = limit / 3 - correction; i < limit / 3; ++i) pi -= sieve[i];

    return pi;
}


int main(int argc, char** argv) {
    if (argc <= 1) {
        std::cout << "Usage: " << argv[0] << " n\n";
        return 0;
    }

    std::cout << num_primes(std::stoi(argv[1])) << "\n";
    return 0;
}

Sulla mia macchina questo funziona in 4,8 secondi per 1907000000 (1,9e9).

Il codice sopra è stato riproposto dalla mia libreria C ++ personale , quindi ho avuto un vantaggio.

Tempi ufficiali

real    0m22.760s
user    0m22.704s
sys 0m0.080s

real    0m22.854s
user    0m22.800s
sys 0m0.077s

real    0m22.742s
user    0m22.700s
sys 0m0.066s

real    0m22.484s
user    0m22.450s
sys 0m0.059s

real    0m22.653s
user    0m22.597s
sys 0m0.080s

real    0m22.665s
user    0m22.602s
sys 0m0.088s

real    0m22.528s
user    0m22.489s
sys 0m0.062s

real    0m22.510s
user    0m22.474s
sys 0m0.060s

real    0m22.819s
user    0m22.759s
sys 0m0.084s

real    0m22.488s
user    0m22.459s
sys 0m0.053s

: o Dayyyum. È veloce. Qual è la tua macchina?
Addison Crump,

@VoteToChiudi Intel i5-4670k con Windows 7. a 64 bit
orlp,

Vuoi aggiungere una spiegazione?
Liam,

@Liam È solo un setaccio che ha un numero che è un multiplo di 2 e 3 lasciato fuori dal setaccio.
orlp,

3

C ++, 2.47215s (29 feb 2016)

Questa è una versione (sciatta) multi-thread della mia altra risposta.

#include <cstdint>
#include <vector>
#include <iostream>
#include <limits>
#include <cmath>
#include <array>
// uses posix ffsll
#include <string.h>
#include <algorithm>
#include <thread>

constexpr uint64_t wheel_width = 2;
constexpr uint64_t buf_size = 1<<(10+6);
constexpr uint64_t dtype_width = 6;
constexpr uint64_t dtype_mask = 63;
constexpr uint64_t buf_len = ((buf_size*wheel_width)>>dtype_width);
constexpr uint64_t seg_len = 6*buf_size;
constexpr uint64_t limit_i_max = 0xfffffffe00000001ULL;

typedef std::vector<uint64_t> buf_type;

void mark_composite(buf_type& buf, uint64_t prime,
                    std::array<uint64_t, 2>& poff,
                    uint64_t seg_start, uint64_t max_j)
{
  const auto p = 2*prime;
  for(uint64_t k = 0; k < wheel_width; ++k)
  {
    for(uint64_t j = 2*poff[k]+(k==0); j < max_j; j += p)
    {
      buf[(j-seg_start)>>dtype_width] |= 1ULL << (j & dtype_mask);
      poff[k] += prime;
    }
  }
}

struct prime_counter
{
  buf_type buf;
  uint64_t n;
  uint64_t seg_a, seg_b;
  uint64_t nj;
  uint64_t store_max;
  uint64_t& store_res;

  prime_counter(uint64_t n, uint64_t seg_a, uint64_t seg_b, uint64_t nj, uint64_t store_max,
                uint64_t& store_res) :
    buf(buf_len), n(n), nj(nj), seg_a(seg_a), seg_b(seg_b),
    store_max(store_max), store_res(store_res)
  {}

  prime_counter(const prime_counter&) = default;
  prime_counter(prime_counter&&) = default;

  prime_counter& operator =(const prime_counter&) = default;
  prime_counter& operator =(prime_counter&&) = default;

  void operator()(uint64_t nsmall_segs,
                  const std::vector<uint64_t>& primes,
                  std::vector<std::array<uint64_t, 2> > poffs)
  {
    uint64_t res = 0;
    // no new prime added portion
    uint64_t seg_start = buf_size*wheel_width*seg_a;
    uint64_t seg_min = seg_len*seg_a+5;

    if(seg_a > nsmall_segs)
    {
      uint64_t max_j = buf_size*wheel_width*nsmall_segs+(seg_a-nsmall_segs)*(buf_len<<dtype_width);
      for(size_t k = 0; k < wheel_width; ++k)
      {
        for(uint64_t i = 0; i < poffs.size() && max_j >= (2*poffs[i][k]+(k==0)); ++i)
        {
          // adjust poffs
          // TODO: might be a more efficient way
          auto w = (max_j-(2*poffs[i][k]+(k==0)));
          poffs[i][k] += primes[i]*(w/(2*primes[i]));
          if(w % (2*primes[i]) != 0)
          {
            poffs[i][k]+=primes[i];// += primes[i]*(w/(2*primes[i])+1);
          }
          /*else
          {

          }*/
        }
      }
    }

    for(uint64_t seg = seg_a; seg < seg_b; ++seg)
    {
      std::fill(buf.begin(), buf.end(), 0);
      const uint64_t limit_i = std::min<uint64_t>((((seg_len+seg_min) >= limit_i_max) ?
                                                   std::numeric_limits<uint32_t>::max() :
                                                   ceil(sqrt(seg_len+seg_min))),
                                                  store_max);
      uint64_t max_j = std::min(seg_start+(buf_len<<dtype_width), nj);
      for(uint64_t i = 0; i < primes.size() && primes[i] <= limit_i; ++i)
      {
        mark_composite(buf, primes[i], poffs[i], seg_start, max_j);
      }
      // sieve
      uint64_t val;
      const uint64_t stop = std::min(seg_min+seg_len, n);
      for(uint64_t i = ffsll(~(buf[0]))-((~buf[0]) != 0)+64*((~buf[0]) == 0);
          (val = 6ULL*(i>>1)+seg_min+2ULL*(i&1ULL)) < stop;)
      {
        if(!(buf[i>>dtype_width] & (1ULL << (i & dtype_mask))))
        {
          ++res;
          ++i;
        }
        else
        {
          uint64_t mask = buf[i>>dtype_width]>>(i&dtype_mask);
          const int64_t inc = ffsll(~mask)-((~mask) != 0)+64*((~mask) == 0);
          i += inc;
        }
      }
      seg_min += seg_len;
      seg_start += buf_size*wheel_width;
    }
    store_res = res;
  }
};

uint64_t num_primes(uint64_t n)
{
  uint64_t res = (n >= 2) + (n >= 3);
  if(n >= 5)
  {
    buf_type buf(buf_len);
    // compute and store primes < sqrt(n)
    const uint64_t store_max = ceil(sqrt(n));

    // only primes >= 5
    std::vector<uint64_t> primes;
    std::vector<std::array<uint64_t, 2> > poffs;
    primes.reserve(ceil(1.25506*store_max/log(store_max)));
    poffs.reserve(ceil(1.25506*store_max/log(store_max)));
    uint64_t seg_start = 0;
    uint64_t seg_min = 5;
    const uint64_t num_segs = 1+(n-seg_min)/seg_len;
    const uint64_t nj = (n-seg_min)/3+1;
    // compute how many small segments there are
    const uint64_t nsmall_segs = 1+(store_max-seg_min)/seg_len;
    for(uint64_t seg = 0; seg < nsmall_segs; ++seg)
    {
      std::fill(buf.begin(), buf.end(), 0);
      // mark off small primes
      const uint64_t limit_i = std::min<uint64_t>((((seg_len+seg_min) >= limit_i_max) ?
                                                   std::numeric_limits<uint32_t>::max() :
                                                   ceil(sqrt(seg_len+seg_min))),
                                                  store_max);
      uint64_t max_j = std::min(seg_start+(buf_len<<dtype_width), nj);
      for(uint64_t i = 0; i < primes.size() && primes[i] <= limit_i; ++i)
      {
        mark_composite(buf, primes[i], poffs[i], seg_start, max_j);
      }
      // sieve
      uint64_t val;
      const uint64_t stop = std::min(seg_min+seg_len, n);
      for(uint64_t i = ffsll(~(buf[0]))-((~buf[0]) != 0)+64*((~buf[0]) == 0);
            (val = 6ULL*(i>>1)+seg_min+2ULL*(i&1ULL)) < stop;)
      {
        if(!(buf[i>>dtype_width] & (1ULL << (i & dtype_mask))))
        {
          if(val <= store_max)
          {
            // add prime and poffs
            primes.push_back(val);
            poffs.emplace_back();
            poffs.back()[0] = (val*val-1)/6-1;
            if(i&1)
            {
              // 6n+1 prime
              poffs.back()[1] = (val*val+4*val-5)/6;
            }
            else
            {
              // 6n+5 prime
              poffs.back()[1] = (val*val+2*val-5)/6;
            }
            // mark-off multiples
            mark_composite(buf, val, poffs.back(), seg_start, max_j);
          }
          ++res;
          ++i;
        }
        else
        {
          uint64_t mask = buf[i>>dtype_width]>>(i&dtype_mask);
          const int64_t inc = ffsll(~mask)-((~mask) != 0)+64*((~mask) == 0);
          i += inc;
        }
      }
      seg_min += seg_len;
      seg_start += buf_size*wheel_width;
    }
    // multi-threaded sieving for remaining segments
    std::vector<std::thread> workers;
    auto num_workers = std::min<uint64_t>(num_segs-nsmall_segs, std::thread::hardware_concurrency());
    std::vector<uint64_t> store_reses(num_workers);

    workers.reserve(num_workers);
    auto num_segs_pw = ceil((num_segs-nsmall_segs)/static_cast<double>(num_workers));
    for(size_t i = 0; i < num_workers; ++i)
    {
      workers.emplace_back(prime_counter(n, nsmall_segs+i*num_segs_pw,
                                         std::min<uint64_t>(nsmall_segs+(i+1)*num_segs_pw,
                                                            num_segs),
                                         nj, store_max, store_reses[i]),
                           nsmall_segs, primes, poffs);
    }
    for(size_t i = 0; i < num_workers; ++i)
    {
      workers[i].join();
      res += store_reses[i];
    }
  }
  return res;
}

int main(int argc, char** argv)
{
  if(argc <= 1)
  {
    std::cout << "usage: " << argv[0] << " n\n";
    return -1;
  }
  std::cout << num_primes(std::stoll(argv[1])) << '\n';
}

Utilizza un setaccio segmentato di Eratostene con una fattorizzazione a ruota di 6 per saltare tutti i multipli di 2/3. Utilizza POSIX ffsllper saltare valori compositi consecutivi.

Compilare:

g++ -std=c++11 -o sieve_mt -O3 -march=native -pthread sieve_mt.cpp

tempistiche non ufficiali

Temporizzato con un Intel i5-6600k su Ubuntu 15.10, il caso 1907000000 ha richiesto 0.817s.

Tempi ufficiali

Per ottenere tempi più precisi, ho cronometrato questo 100 volte, quindi ho diviso il tempo per 100.

real    4m7.215s
user    23m54.086s
sys 0m1.239s

Dato che questa e la risposta di Python di @Dennis sono così vicine, posso ripeterle per risultati più accurati.
Liam,

Wow wow wow. Questo ha ancora meno senso per me di CJam o Pyth. Lo chiamerò il mostro bit-shift! +1
Tamoghna Chowdhury

A parte, potresti provare CUDA / OpenCL per uno speedup GPU? Se avessi saputo di più C, avrei potuto.
Tamoghna Chowdhury,

Sì, immagino di essere stato un po 'eccessivo con il bitshifting / il mascheramento: PI non so se GPGPU sarebbe utile qui o no; l'unica area che riesco a vedere è forse di aiuto nel pre-setacciamento di piccoli numeri primi, e anche in questo caso la velocità di trasferimento dei dati potrebbe essere sufficiente per ucciderlo. Quello che mi fa ancora male è che sono ancora fuori di un fattore di circa 10 dall'implementazione del setaccio più veloce che abbia mai visto
helloworld922

2

C, 2m42.7254s (28 feb 2016)

Salva come pi.c, compila come gcc -o pi pi.c, esegui come ./pi <arg>:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

unsigned char p[2000000001];

int main(int argc, char **argv)
{
        unsigned int n, c, i, j;

        n = atoi(argv[1]);
        memset(p, 1, n + 1);

        p[1] = p[0] = 0;

        for (i = 2, c = 0; i <= n; i++)
        {
                if (p[i])
                {
                        c++;
                        for (j = i + i; j <= n; j += i)
                                p[j] = 0;
                }
        }

        printf("%d: %d\n", n, c);

        return 0;
}

Ha bisogno di molta memoria per funzionare! Se il tuo hardware non può risparmiare fino a due gigabyte di memoria reale, il programma si arresterà in modo anomalo o funzionerà molto lentamente a causa del thrashing di VMM e HD.

Il tempismo approssimativo sul mio hardware è 1.239 × 10 -8 · n 1.065 s. Ad esempio, un input di n = 2 × 10 9 richiede circa 100 s per l'esecuzione.

Tempi ufficiali

real    2m42.657s
user    2m42.065s
sys 0m0.757s

real    2m42.947s
user    2m42.400s
sys 0m0.708s

real    2m42.827s
user    2m42.282s
sys 0m0.703s

real    2m42.800s
user    2m42.300s
sys 0m0.665s

real    2m42.562s
user    2m42.050s
sys 0m0.675s

real    2m42.788s
user    2m42.192s
sys 0m0.756s

real    2m42.631s
user    2m42.074s
sys 0m0.720s

real    2m42.658s
user    2m42.115s
sys 0m0.707s

real    2m42.710s
user    2m42.219s
sys 0m0.657s

real    2m42.674s
user    2m42.110s
sys 0m0.730s

Questo funziona usando il setaccio di eratostene? Lo farò quando torno a casa
Liam, il

Mi sto confondendo sul primo caso (altri vanno bene). Succede dopo ~ 1 minuto di runtime. Ho aggiunto una if (p==NULL) {exit(1);}riga al codice, quindi non credo che il malloc stia fallendo (inoltre fallirebbe all'inizio, non dopo 1 minuto). Idee su cosa sta succedendo?
Liam,

Molti sistemi, incluso Linux, eseguono un'allocazione ottimistica. Ad esempio, se chiedi 1 GB, "te lo darà", ma quando effettivamente lo userai, e se il sistema non lo trova, andrà in crash. Se così fosse, probabilmente si bloccherebbe al memset. Il minuto che ci vuole è il tempo impiegato a cercare di fondere l'heap in un blocco contiguo. Controlla anche che il tuo sistema sia sizeof (bool) == 1. Se è == 4, allora posso riscriverlo per usare char.

Ho già controllato. Bool è di 1 byte. È possibile semplicemente richiedere il modulo 2 * 10 ^ 9 byte di memoria nello stack? Vale a dire una variabile globale che (su gcc) credo che verrà avviata a 0. Ciò richiederebbe charinvece l' uso, anche se penso.
Liam,

1
@Liam Difficile a dirsi. L'overflow di numeri interi con segno è un comportamento indefinito, quindi senza guardare l'assembly generato, è difficile prevedere cosa ha fatto il compilatore.
Dennis,

2

Julia, 1m 21.1329s

Vorrei inventare qualcosa un po 'più veloce, ma per ora ecco un'implementazione piuttosto ingenua del setaccio di Eratostene.

function eratos(n::Int64)
    sieve = trues(n)
    sieve[1] = false
    for p = 2:isqrt(n)
        @inbounds sieve[p] || continue
        for i = 2:n÷p
            @inbounds sieve[p*i] = false
        end
    end
    return sum(sieve)
end

const x = parse(Int64, ARGS[1])

println(eratos(x))

Scarica l'ultima versione di Giulia per il sistema qui . Assicurati che l'eseguibile Julia sia nel tuo percorso. Salvare il codice come sieve.jled eseguire dalla riga di comando come julia sieve.jl N, dove si Ntrova l'input.

Tempi ufficiali

real    1m21.227s
user    1m20.755s
sys 0m0.576s

real    1m20.944s
user    1m20.426s
sys 0m0.640s

real    1m21.052s
user    1m20.581s
sys 0m0.573s

real    1m21.328s
user    1m20.862s
sys 0m0.570s

real    1m21.253s
user    1m20.780s
sys 0m0.588s

real    1m20.925s
user    1m20.460s
sys 0m0.576s

real    1m21.011s
user    1m20.512s
sys 0m0.601s

real    1m21.011s
user    1m20.550s
sys 0m0.564s

real    1m20.875s
user    1m20.409s
sys 0m0.569s

real    1m21.703s
user    1m21.088s
sys 0m0.701s

1
Ho implementato il setaccio di Atkin e la mia implementazione è più lenta. >: U
Alex A.

@Liam Whoa. Mi chiedo perché i tempi ufficiali siano molto più lunghi di quelli non ufficiali. I tempi ufficiali sono piuttosto orribili.
Alex A.

Bene, i tempi ufficiali sono per tutti i casi di punteggio insieme. Quelli non ufficiali vanno numero per numero. Inoltre, il mio computer probabilmente non è veloce come il tuo.
Liam,

@Liam Oh, ha più senso. Dang, ho pensato che fosse decente. Vabbè, torniamo al tavolo da disegno.
Alex A.

Sto per rubare l'algoritmo di Dennis ... solo per capire quanto è veloce.
Liam,

2

Java, 42.663122s * (3 marzo 2016)

* questo è stato cronometrato internamente dal programma (sul computer del PO però)

public class PrimeCounter
{
public static final String START_CODE="=",
TEST_FORMAT="Input = %d , Output = %d , calculated in %f seconds%n",
PROMPT="Enter numbers to compute pi(x) for (Type \""+START_CODE+"\" to start):%n",
WAIT="Calculating, please wait...%n",
WARNING="Probably won't work with values close to or more than 2^31%n",
TOTAL_OUTPUT_FORMAT="Total time for all inputs is %f seconds%n";
public static final int NUM_THREADS=16,LOW_LIM=1,HIGH_LIM=1<<28;
private static final Object LOCK=new Lock();
private static final class Lock{}
/**
 * Generates and counts primes using an optimized but naive iterative algorithm.
 * Uses MultiThreading for arguments above LOW_LIM
 * @param MAX : argument x for pi(x), the limit to which to generate numbers.
 */
public static long primeCount(long MAX){
    long ctr=1;
    if(MAX<1<<7){
        for(long i=3;i<=MAX;i+=2){
            if(isPrime(i))++ctr;
        }
    }else{
        long[] counts=new long[NUM_THREADS];
        for(int i=0;i<NUM_THREADS;++i){
            counts[i]=-1;
        }
        long range=Math.round((double)MAX/NUM_THREADS);
        for(int i=0;i<NUM_THREADS;++i){
            long start=(i==0)?3:i*range+1,end=(i==NUM_THREADS-1)?MAX:(i+1)*range;
            final int idx=i;
            new Thread(new Runnable(){
                    public void run(){
                        for(long j=start;j<=end;j+=2){
                            if(isPrime(j))++counts[idx];
                        }
                    }
                }).start();
        }
        synchronized(LOCK){
            while(!completed(counts)){
                try{
                    LOCK.wait(300);}catch(InterruptedException ie){}
            }
            LOCK.notifyAll();
        }
        for(long count:counts){
            ctr+=count;
        }
        ctr+=NUM_THREADS;
    }
    return ctr;
}

/**
 * Checks for completion of threads
 * @param array : The array containing the completion data
 */
private static boolean completed(long[] array){
    for(long i:array){
        if(i<0)return false;
    }return true;
}

/**
 * Checks if the parameter is prime or not.
 * 2,3,5,7 are hardcoded as factors.
 * @param n : the number to check for primality
 */
private static boolean isPrime(long n){
    if(n==2||n==3||n==5||n==7)return true;
    else if(n%2==0||n%3==0||n%5==0||n%7==0)return false;
    else{
        for(long i=11;i<n;i+=2){
            if(n%i==0)return false;
        }
        return true;
    }
}

/**
 * Calculates primes using the atandard Sieve of Eratosthenes.
 * Uses 2,3,5,7 wheel factorization for elimination (hardcoded for performance reasons)
 * @param MAX : argument x for pi(x)
 * Will delegate to <code>primeCount(long)</code> for MAX<LOW_LIM and to <code>bitPrimeSieve(long)</code>
 * for MAX>HIGH_LIM, for performance reasons.
 */
public static long primeSieve(long MAX){
    if(MAX<=1)return 0;
    else if(LOW_LIM>0&&MAX<LOW_LIM){return primeCount(MAX);}
    else if(HIGH_LIM>0&&MAX>HIGH_LIM){return bitPrimeSieve(MAX);}
    int n=(int)MAX;
    int sn=(int)Math.sqrt(n),ctr=2;
    if(sn%2==0)--sn;
    boolean[]ps=new boolean[n+1];
    for(int i=2;i<=n;++i){
        if(i==2||i==3||i==5||i==7)ps[i]=true;
        else if(i%2!=0&&i%3!=0&&i%5!=0&&i%7!=0)ps[i]=true;
        else ++ctr;
    }
    for(int i=(n>10)?11:3;i<=sn;i+=2){
        if(ps[i]){
            for(int j=i*i;j<=n;j+=i){
                if(ps[j]){ ps[j]=false;++ctr;}
            }
        }
    }
    return (n+1-ctr);
}
/**
 * Calculates primes using bitmasked Sieve of Eratosthenes.
 * @param MAX : argument x for pi(x)
 */
public static long bitPrimeSieve(long MAX) {
    long SQRT_MAX = (long) Math.sqrt(MAX);
    if(SQRT_MAX%2==0)--SQRT_MAX;
    int MEMORY_SIZE = (int) ((MAX+1) >> 4);
    byte[] array = new byte[MEMORY_SIZE];
    for (long i = 3; i <= SQRT_MAX; i += 2) {
        if ((array[(int) (i >> 4)] & (byte) (1 << ((i >> 1) & 7))) == 0) {
            for(long j=i*i;j<=MAX;j+=i<<1) {
                if((array[(int) (j >> 4)] & (byte) (1 << ((j >> 1) & 7))) == 0){
                    array[(int) (j >> 4)] |= (byte) (1 << ((j >> 1) & 7));
                }
            }
        }
    }
    long pi = 1;
    for (long i = 3; i <= MAX; i += 2) {
        if ((array[(int) (i >> 4)] & (byte) (1 << ((i >> 1) & 7))) == 0) {
            ++pi;
        }
    }
    return pi;
}
/**
 * Private testing and timer function
 * @param MAX : input to be passed on to <code>primeSieve(long)</code>
 */
private static long sieveTest(long MAX){
    long start=System.nanoTime();
    long ps=primeSieve(MAX);
    long end=System.nanoTime();
    System.out.format(TEST_FORMAT,MAX,ps,((end-start)/1E9));
    return end-start;
}
/**
 * Main method: accepts user input and shows total execution time taken
 * @param args : The command-line arguments
 */
public static void main(String[]args){
    double total_time=0;
    java.util.Scanner sc=new java.util.Scanner(System.in);
    java.util.ArrayList<Long> numbers=new java.util.ArrayList<>();
    System.out.format(PROMPT+WARNING);
    String line=sc.nextLine();
    while(!line.equals(START_CODE)/*sc.hasNextLine()&&Character.isDigit(line.charAt(0))*/){
        numbers.add(Long.valueOf(line));
        line=sc.nextLine();
    }
    System.out.format(WAIT);
    for(long num:numbers){
        total_time+=sieveTest(num);
    }
    System.out.format(TOTAL_OUTPUT_FORMAT,total_time/1e9);
}
}

Segue la grande tradizione PPCG del codice auto-documentante (anche se non in senso letterale: p).

Questo per dimostrare che Java può essere abbastanza veloce da essere competitivo con altri linguaggi VM quando si usano algoritmi simili.

Esegui informazioni

Eseguilo come avresti la risposta di @ CoolestVeto, ma il mio non ha bisogno di argomenti da riga di comando, può ottenerli da STDIN.

Modifica la NUM_THREADScostante per impostarlo su 2 volte il numero di core nativo per le massime prestazioni (come ho osservato - Nel mio caso ho 8 core virtuali quindi è impostato su 16, l'OP potrebbe volerne 12 per il suo processore hexa-core).

Quando ho eseguito questi test ho usato JDK 1.7.0.45 con BlueJ 3.1.6 (IntelliJ si stava aggiornando) su Windows 10 Enterpise x64 su un laptop ASUS K55VM (Core i7 3610QM, 8 GB di RAM). Google Chrome 49.0 a 64 bit con 1 scheda (PPCG) aperta e il download di 1 file QBittorrent erano in esecuzione in background, utilizzo del 60% di RAM all'inizio della corsa.

Fondamentalmente,

javac PrimeCounter.java
java PrimeCounter

Il programma ti guiderà attraverso il resto.

Il tempismo è fatto da Java integrato System.nanoTime().

Dettagli algoritmo:

Dispone di 3 varianti per diversi casi d'uso: una versione ingenua come @ CoolestVeto (ma multithread) per input inferiori a 2 ^ 15, e un setaccio a maschera di bit di Eratostene con eliminazione dispari per input superiori a 2 ^ 28 e un setaccio normale di Eratostene con un Fattorizzazione delle ruote 2/3/5/7 per la preeliminazione dei multipli.

Uso il setaccio bitmasked per evitare argomenti JVM speciali per i casi di test più grandi. Se ciò è possibile, è possibile eliminare l'overhead per il calcolo del conteggio nella versione bitmasked.

Ecco l'output:

Enter numbers to compute pi(x) for (Type "=" to start):
Probably won't work with values close to or more than 2^31
41500
24850000
40550000
99820000
660000000
1240000000
1337000000
1907000000
=
Calculating, please wait...
Input = 41500 , Output = 4339 , calculated in 0.002712 seconds
Input = 24850000 , Output = 1557132 , calculated in 0.304792 seconds
Input = 40550000 , Output = 2465109 , calculated in 0.523999 seconds
Input = 99820000 , Output = 5751639 , calculated in 1.326542 seconds
Input = 660000000 , Output = 34286170 , calculated in 4.750049 seconds
Input = 1240000000 , Output = 62366021 , calculated in 9.160406 seconds
Input = 1337000000 , Output = 66990613 , calculated in 9.989093 seconds
Input = 1907000000 , Output = 93875448 , calculated in 14.832107 seconds
Total time for all inputs is 40.889700 seconds

Emettere solo il risultato di pi (n) (senza prompt) può far risparmiare un po 'di tempo, perché STDOUT è ... beh, diciamo che potrebbe essere solo un po' più veloce.
user48538

@ zyabin101, se qualcuno avesse avuto la pazienza di esaminare il codice, avrebbe capito che la latenza di STDOUT è stata presa in considerazione.
Tamoghna Chowdhury

Anche per i tempi, ho inviato stdout a / dev / null
Liam

@Liam Immagino che dovrai fare un'eccezione nel mio caso, quindi. È possibile modificare il metodo principale per gli argomenti della riga di comando, ma il programma è comunque auto-temporizzato. Dai un'occhiata comunque. Per favore?
Tamoghna Chowdhury

Certo che lo farò. Lo farò domani. Se ho problemi, ti pingerò in chat
Liam

2

Python 3

import sys

sys.setrecursionlimit(sys.maxsize)

n = int(sys.argv[-1])

if n < 4:
    print(0 if n < 2 else n-1)
    exit()

p = [0, 0] + [True] * n

i = 0
while i < pow(n, 0.5):
    if p[i]:
        j = pow(i, 2)
        while j < n:
            p[j] = False
            j += i
    i += 1

print(sum(p) - 2)

Usa il setaccio di Eratostene. Funziona a una media di 8.775sdove n = 10^7. A quel tempo, ho usato il timecomando incorporato . Per esempio:

$ time python3 test.py 90
24

real    0m0.045s
user    0m0.031s
 sys    0m0.010s

È il setaccio! Non potevo usarlo in Java perché non mi piaceva la quantità di memoria utilizzata da un array booleano. D:
Addison Crump,

errore di memoria nei casi più grandi.
Liam,

Quali casi? Credo di averlo risolto. @Liam
Zach Gates,

2
@VoteToClose Quindi non utilizzare un array booleano. Utilizzare un array intero e lo spostamento / mascheramento dei bit, con ciascun bit che rappresenta un valore booleano.
mbomb007,

AttributeError: 'module' object has no attribute 'maxint'
Dennis,

1

C ++, 9.3221s (29 feb 2016)

#include <cstdint>
#include <vector>
#include <iostream>
#include <limits>
#include <cmath>
#include <array>
// uses posix ffsll
#include <string.h>
#include <algorithm>

constexpr uint64_t wheel_width = 2;
constexpr uint64_t buf_size = 1<<(10+6);
constexpr uint64_t dtype_width = 6;
constexpr uint64_t dtype_mask = 63;
constexpr uint64_t buf_len = ((buf_size*wheel_width)>>dtype_width);

typedef std::vector<uint64_t> buf_type;

void mark_composite(buf_type& buf, uint64_t prime,
                    std::array<uint64_t, 2>& poff,
                    uint64_t seg_start, uint64_t max_j)
{
  const auto p = 2*prime;
  for(uint64_t k = 0; k < wheel_width; ++k)
  {
    for(uint64_t j = 2*poff[k]+(k==0); j < max_j; j += p)
    {
      buf[(j-seg_start)>>dtype_width] |= 1ULL << (j & dtype_mask);
      poff[k] += prime;
    }
  }
}

uint64_t num_primes(uint64_t n)
{
  uint64_t res = (n >= 2) + (n >= 3);
  if(n >= 5)
  {
    buf_type buf(buf_len);
    // compute and store primes < sqrt(n)
    const uint64_t store_max = ceil(sqrt(n));

    // only primes >= 5
    std::vector<uint64_t> primes; // 5,7,11
    std::vector<std::array<uint64_t, 2> > poffs;// {{3,0},{0,5},{8,1}};
    primes.reserve(ceil(1.25506*store_max/log(store_max)));
    poffs.reserve(ceil(1.25506*store_max/log(store_max)));
    uint64_t seg_start = 0;
    uint64_t seg_min = 5;
    constexpr uint64_t seg_len = 6*buf_size;///wheel_width;
    constexpr uint64_t limit_i_max = 0xfffffffe00000001ULL;
    const uint64_t num_segs = 1+(n-seg_min)/seg_len;
    const uint64_t nj = (n-seg_min)/3+1;
    for(uint64_t seg = 0; seg < num_segs; ++seg)
    {
      std::fill(buf.begin(), buf.end(), 0);
      // mark off small primes
      const uint64_t limit_i = std::min<uint64_t>((((seg_len+seg_min) >= limit_i_max) ?
                                                   std::numeric_limits<uint32_t>::max() :
                                                   ceil(sqrt(seg_len+seg_min))),
                                                  store_max);
      uint64_t max_j = std::min(seg_start+(buf_len<<dtype_width), nj);
      for(uint64_t i = 0; i < primes.size() && primes[i] <= limit_i; ++i)
      {
        mark_composite(buf, primes[i], poffs[i], seg_start, max_j);
      }
      // sieve
      uint64_t val;
      const uint64_t stop = std::min(seg_min+seg_len, n);
      for(uint64_t i = ffsll(~(buf[0]))-((~buf[0]) != 0)+64*((~buf[0]) == 0);
            (val = 6ULL*(i>>1)+seg_min+2ULL*(i&1ULL)) < stop;)
      {
        if(!(buf[i>>dtype_width] & (1ULL << (i & dtype_mask))))
        {
          if(val <= store_max)
          {
            // add prime and poffs
            primes.push_back(val);
            poffs.emplace_back();
            poffs.back()[0] = (val*val-1)/6-1;
            if(i&1)
            {
              // 6n+1 prime
              poffs.back()[1] = (val*val+4*val-5)/6;
            }
            else
            {
              // 6n+5 prime
              poffs.back()[1] = (val*val+2*val-5)/6;
            }
            // mark-off multiples
            mark_composite(buf, val, poffs.back(), seg_start, max_j);
          }
          ++res;
          ++i;
        }
        else
        {
          uint64_t mask = buf[i>>dtype_width]>>(i&dtype_mask);
          const int64_t inc = ffsll(~mask)-((~mask) != 0)+64*((~mask) == 0);
          i += inc;
        }
      }
      seg_min += seg_len;
      seg_start += buf_size*wheel_width;
    }
  }
  return res;
}

int main(int argc, char** argv)
{
  if(argc <= 1)
  {
    std::cout << "usage: " << argv[0] << " n\n";
    return -1;
  }
  std::cout << num_primes(std::stoll(argv[1])) << '\n';
}

Utilizza un setaccio segmentato di Eratostene con una fattorizzazione a ruota di 6 per saltare tutti i multipli di 2/3. Utilizza POSIX ffsllper saltare valori compositi consecutivi.

Potenzialmente potrebbe essere velocizzato facendo funzionare il setaccio segmentato in parallelo.

Compilare:

g++ -std=c++11 -o sieve -O3 -march=native sieve.cpp

tempistiche non ufficiali

Temporizzato con un Intel i5-6600k su Ubuntu 15.10, il caso 1907000000 ha richiesto 2.363s.

41500
4339

real    0m0.001s
user    0m0.000s
sys     0m0.000s

24850000
1557132

real    0m0.036s
user    0m0.032s
sys     0m0.000s

40550000
2465109

real    0m0.056s
user    0m0.052s
sys     0m0.000s

99820000
5751639

real    0m0.149s
user    0m0.144s
sys     0m0.000s

660000000
34286170

real    0m0.795s
user    0m0.788s
sys     0m0.000s

1240000000
62366021

real    0m1.468s
user    0m1.464s
sys     0m0.000s

1337000000
66990613

real    0m1.583s
user    0m1.576s
sys     0m0.004s

1907000000
93875448

real    0m2.363s
user    0m2.356s
sys     0m0.000s

Tempi ufficiali

real    0m9.415s
user    0m9.414s
sys 0m0.014s

real    0m9.315s
user    0m9.315s
sys 0m0.013s

real    0m9.307s
user    0m9.309s
sys 0m0.012s

real    0m9.333s
user    0m9.330s
sys 0m0.017s

real    0m9.288s
user    0m9.289s
sys 0m0.012s

real    0m9.319s
user    0m9.318s
sys 0m0.015s

real    0m9.285s
user    0m9.284s
sys 0m0.015s

real    0m9.342s
user    0m9.342s
sys 0m0.014s

real    0m9.305s
user    0m9.305s
sys 0m0.014s

real    0m9.312s
user    0m9.313s
sys 0m0.012s
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.