Calculer le nombre de nombres premiers jusqu'à n


64

π ( n ) est le nombre de nombres premiers inférieurs ou égaux à n .

Entrée: un nombre naturel, n .

Sortie: π (n).

Scoring: C'est un défi de . Le score sera la somme des temps pour les cas de score. Je vais chronométrer chaque entrée sur mon ordinateur.

Règles et détails

  • Votre code devrait fonctionner pour n jusqu'à 2 milliards (2.000.000.000).

  • Les éléments intégrés qui banalisent cela ne sont pas autorisés. Ceci inclut des fonctions π intégrées ou des listes de valeurs pour π ( n ).

  • Les fonctions intégrées qui testent la primalité ou génèrent des nombres premiers ne sont pas autorisées. Cela inclut les listes de nombres premiers, qui ne peuvent pas être recherchées de manière externe ni codées en dur localement, sauf en ce qui concerne le point suivant.

  • Vous pouvez coder en dur les nombres premiers jusqu'à 19 inclus et pas plus.

  • votre implémentation de π devrait être déterministe. Cela signifie que pour un n spécifique , votre code doit être exécuté dans le même temps (environ).

  • Les langues utilisées doivent être librement disponibles sur Linux (Centos 7). Des instructions doivent être incluses sur la façon d’exécuter votre code. Inclure les détails du compilateur / interprète si nécessaire.

  • Les temps officiels seront de mon ordinateur.

  • Lors de la publication, veuillez inclure un temps auto-mesuré sur certains cas / tous les cas de test / score, juste pour me donner une estimation de la vitesse à laquelle votre code est exécuté.

  • Les soumissions doivent correspondre à une réponse à cette question.

  • Je suis en cours d'exécution 64bit centos7. Je n'ai que 8 Go de RAM et 1 Go d'échange. Le modèle de processeur est le suivant: Processeur à six cœurs AMD FX (tm) -6300.

Cas de test ( source ):

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

Scores de cas ( même source )

Comme d'habitude, ces cas sont sujets à changement. L'optimisation pour les cas de scoring n'est pas autorisée. Je peux aussi changer le nombre de cas dans le but d'équilibrer des temps d'exécution raisonnables et des résultats précis.

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

Durée

Comme il s’agit d’un défi de et que les entrées doivent être exécutées sur mon ordinateur, je me réserve le droit d’arrêter le chronométrage des entrées après 2 semaines. Passé ce délai, les entrées sont toujours acceptées, mais rien ne garantit qu’elles sont chronométrées officiellement.

Cela dit, je n’attends pas trop de réponses à ce défi et je vais probablement continuer à chronométrer de nouvelles réponses indéfiniment.

Notation des détails

J'ai chronométré les entrées plus rapides avec le script suivant:

#!/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écrit à stderr, alors j'ai envoyé stderrà un fichier journal en utilisant exec 2 >> <filename>. Vous remarquerez peut-être que cela stdoutest envoyé à/dev/null . Ce n'est pas un problème, car j'ai déjà vérifié que les programmes produisaient le bon résultat.

J'ai exécuté le timeall.shscript ci-dessus 10 fois en utilisantfor i in {1..10}; do ./timeall.sh; done;

J'ai ensuite fait la moyenne real time score pour chaque entrée.

Notez qu'aucun autre programme ne fonctionnait sur mon ordinateur pendant le chronométrage.

De plus, les temps officiels ont été ajoutés à chaque entrée. S'il vous plaît vérifiez votre propre moyenne.


Qu'est-ce qui nous empêche d'utiliser une table de correspondance avec les premières 2e9 valeurs de pi (n)? Cela serait-il acceptable? ( Je ne sais pas comment il serait rapide soit, cependant, parce que ce serait une grande table)
Luis Mendo

@DonMuesli Cela ne serait pas acceptable (cela va à l'encontre de l'esprit du défi), j'ai édité pour le rendre expressément interdit également maintenant.
Liam

8
Il est dangereux de se référer à "l'esprit" du défi. Votre "contre-esprit" peut être le "grand tour" de quelqu'un d'autre :-) Il vaut mieux que vous l'expliquiez
Luis Mendo

1
Qu'est-ce qu'un construit? J'ai une fonction de liste de nombres premiers dans une bibliothèque. Puis-je l'utiliser? Sinon, puis-je copier le code source de la bibliothèque dans mon programme et l'utiliser?
nimi

1
@Liam: Oui, je sais, mais qu'est-ce qui est intégré? La copie du code source d'une bibliothèque est-elle intégrée?
nimi

Réponses:


119

C, 0.026119s (12 mars 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);
}

Ceci utilise la méthode de Meissel-Lehmer .

Les horaires

Sur ma machine, il me faut environ 5,7 millisecondes pour les cas de test combinés. Ceci est sur un Intel Core i7-3770 avec DDR3 RAM à 1867 MHz, exécutant 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

Comme la variance est devenue trop importante , j'utilise des timings du programme pour les temps d'exécution non officiels. C'est le script qui calcule la moyenne des temps d'exécution combinés.

#!/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

Temps officiels

Ce temps est pour faire les cas de partition 1000 fois.

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

Comment ça fonctionne

Formule

Soit un entier positif.x

Chaque entier positif vérifie exactement l'une des conditions suivantes.nx

  1. n=1

  2. est divisible par un nombre premier p dans [ 1 , 3 np.[1,x3]

  3. , où p et q sont des nombres premiers (non nécessairement distincts) dans ( 3 n=pqpq.(x3,x23)

  4. est premier et n > 3 nn>x3

Soit le nombre de nombres premiers p tels que p y . Il y a π ( x ) - π ( 3 π(y)ppynombres qui entrent dans la quatrième catégorie.π(x)π(x3)

Soit la quantité d'entiers positifs m y qui est un produit de k nombres premiers exactement et non parmi les c premiers nombres premiers. Il y a P 2 ( x , π ( 3 Pk(y,c)mykcP2(x,π(x3)) numéros qui appartiennent à la troisième catégorie.

Enfin, soit désigner la quantité d'entiers positifs k y qui sont premiers entre eux les premier c nombres premiers. Il y a x - ϕ ( x , π ( 3 ϕ(y,c)kycxϕ(x,π(x3)) numéros entrant dans la deuxième catégorie.

Puisqu'il y a nombres dans toutes les catégories,x

1+xϕ(x,π(x3))+P2(x,π(x3))+π(x)π(x3)=x

et donc,

π(x)=ϕ(x,π(x3))+π(x3)1P2(x,π(x3))

Les nombres de la troisième catégorie ont une représentation unique si nous exigeons que et donc p pq . De cette façon, le produit des nombres premierspetqest dans la troisième catégorie si et seulement si 3 pXpq , donc il y aπ(xx3<pqxpvaleurs possibles pourqpour une valeur fixe dep, etP2(x,π(3π(xp)π(p)+1qp, oùpkdésigne lekthP2(x,π(x3))=π(x3)<kπ(x)(π(xpk)π(pk)+1)pkkth nombre premier.

Enfin, tout entier positif qui ne correspond pas aux c premiers nombres premiers peut être exprimé de manière unique par n = p k f , où p k est le facteur premier le plus bas de n . De cette façon, k c et f est coprime des k - 1 premiers nombres premiers.nycn=pkfpknkcfk1

Ceci conduit à la formule récursive . En particulier, la somme est vide sic=0, doncϕ(y,0)=y.ϕ(y,c)=y1kcϕ(ypk,k1)c=0ϕ(y,0)=y

Nous avons maintenant une formule qui nous permet de calculer en ne générant que le premier π ( 3 π(x)π(x23) nombres premiers (millions vs milliards).

Algorithme

Nous aurons besoin de calculer , oùppeut être aussi bas que3π(xp)p . Bien qu’il existe d’autres moyens de le faire (comme l’application récursive de notre formule), le moyen le plus rapide semble être d’énumérer tous les nombres premiers jusqu’à3 √.x3x23 , ce qui peut être fait avec le tamis d'Eratosthenes.

Premièrement, nous identifions et stockons tous les nombres premiers dans et calculerπ( 3 [1,x]etπ(π(x3)en même temps. Ensuite, on calcule xπ(x) pour toutkin(π(3xpkk, et comptez les nombres premiers jusqu'à chaque quotient successif.(π(x3),π(x)]

Aussi, a la forme ferméeπ( 3 π(x3)<kπ(x)(π(pk)+1) , ce qui nous permet de compléter le calcul deP2(x,π(3π(x3)π(x))(π(x3)+π(x)12P2(x,π(x3)) .

Cela laisse le calcul de , qui est la partie la plus chère de l’algorithme. Simplement en utilisant la formule récursive , il faudrait deux c des appels de fonction pour calculer φ ( y , c )ϕ2cϕ(y,c) .

Tout d' abord, pour toutes les valeurs de c , de sorte que φ ( y , c ) = y - Σ 1 k c , p ky φ ( yϕ(0,c)=0c. En soi, cette observation est déjà suffisante pour rendre le calcul possible. En effet, tout nombre inférieur à2×109est plus petit que le produit de dix nombres premiers distincts, de sorte que la très grande majorité des sommands disparaissent.φ(y,c)=y-Σ1kc,pkyφ(ypk,k-1)2dix9

De plus, en regroupant les et les premiers c ' summands de la définition de φ , on obtient la solution formule φ ( y , c ) = φ ( y , c ' ) - Σ c ' < k c , p ky φ ( yycϕ. Ainsi, précalculerφ(y,c')pour une durée déterminéec'etvaleurs appropriées deyϕ(y,c)=ϕ(y,c)c<kc,pkyϕ(ypk,k1)ϕ(y,c)cy enregistre la plupart des appels de fonctions restantes et les calculs associés.

Si , puis φ ( m c , c ) = φ ( m c ) , étant donnéles nombres entiers [ 1 , m c ] qui sont divisibles par aucun de p 1 , , p c sont précisément ceux qui sont coprime à m c . Aussi, depuis gcd ( z + m c , mmc=1kcpkϕ(mc,c)=φ(mc)[1,mc]p1,,pcmc , on a que φ ( y , c ) = φ ( ygcd(z+mc,mc)=gcd(z,mc)ϕ(y,c)=ϕ(ymcmc,c)+ϕ(y .

Depuis la fonction indicatrice d'Euler est multiplicatif, , et nous avons un moyen facile de calculer φ ( y , c ) pour tout y en précalculer les valeurs que ceux y dans [ 0 , m c )φ(mc)=1kcφ(pk)=Π1kc(pk-1)φ(y,c)yy[0,mc) .

En outre, si l' on pose , on obtient φ ( y , c ) = φ ( y , c - 1 ) - φ ( yc=c-1, la définition originale de l'article de Lehmer. Cela nous donne un moyen simple de pré-calculerϕ(y,c)pour des valeurs croissantes dec.φ(y,c)=φ(y,c-1)-φ(ypc,c-1)φ(y,c)c

En plus du précalcul pour une certaine valeur faible de c , nous le précalculons également pour des valeurs faibles de yϕ(y,c)cy , coupant la récursivité après avoir atteint un certain seuil.

la mise en oeuvre

La section précédente couvre la plupart des parties du code. Un détail important restant est la manière dont les divisions dans la fonctionPhi sont effectuées.

Etant donné que le calcul du ne nécessite en divisant par le premier π ( 3 ϕnombres premiers, nous pouvons utiliser lafonction à la place. Plutôt que de simplement diviser unypar un nombre premierp, on multiplieypardp 2 64π(x3)fastdivypy place et récupèreydp264p commedpyyp . En raison de la manière dont la multiplication d’entiers est implémentée surx64, iln’est pas nécessaire dediviser par264; les 64 bits les plus élevés dedpydpy264264dpy sont stockées dans leur propre registre.

Notez que cette méthode nécessite un calcul préalable , qui n’est pas plus rapide que le calcul ydp directement. Cependant, étant donné que nous devons diviser encore et encore par les mêmes nombres premiers et que la division esttellement plus lenteque la multiplication, il en résulte une accélération importante. On peut trouver plus de détails sur cet algorithme, ainsi qu'une preuve formelle, dansDivision par entiers invariants à l'aide de la multiplication.yp


22
On ne dépasse pas simplement Dennis?
Addison Crump

8
Honnêtement, je n'arrive pas à croire à quelle vitesse. Je n'ai pas eu le temps de comprendre ce qui se passe, mais j'en ai vraiment besoin.
Liam

27
@Liam, j'ai bien l'intention d'expliquer comment cela fonctionne, mais j'essaie encore de l'accélérer. En ce moment, j'aimerais vraiment que PPCG ait LaTeX ...
Dennis,

15
Note amusante: (sur ma machine), cela bat actuellement à la fois les fonctions internes de Mathematica et de kimwalisch dans la bibliothèque primecount C ++ de github.
Michael Klein

10
@TheNumberOne chut ne lui en parlez pas ... d'autres personnes pourraient en avoir besoin pour le vaincre
Liam

24

C99 / C ++, 8.9208s (28 févr. 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) );
    }

Implémentation d'un tamis d'erastothenes à base de bitmap. Il effectue les étapes suivantes:

  1. Commencez par générer un motif de bits répété pour remplir le tamis, couvrant des multiples de 2,3,5,7.
  2. Ensuite, utilisez la méthode sieve pour générer un tableau de tous les nombres premiers inférieurs à sqrt (n).
  3. Ensuite, utilisez la liste principale de l'étape précédente pour écrire dans le tamis. Cette opération est effectuée sur des fragments du tamis qui ont à peu près la taille d'un cache L1, de sorte que le traitement du tamis ne supprime pas constamment le cache L1; cela semble entraîner une accélération de 5x par rapport à la non-segmentation.
  4. Enfin, effectuez un nombre de bits.

Compilé avec gcc primecount.c -O3 -lm -Wallet exécuté sur ubuntu 15.10 (64 bits) sur un i7-4970k, il faut environ 2,2 secondes pour l’ensemble des cas de partition. Le temps d'exécution est dominé par l'étape 3; cela pourrait être multithread si vous le souhaitez, car les morceaux sont indépendants; cela nécessiterait quelques précautions pour s'assurer que les limites des blocs sont correctement alignées.

Il alloue un peu plus de mémoire que strictement nécessaire pour le tamis; cela laisse de la place à un dépassement de fin de mémoire tampon, qui est nécessaire pour que la boucle déroulée à l'étape 3 fonctionne correctement.

Temps officiels

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
Bienvenue dans Programming Puzzles & Code Golf, et félicitations pour cet excellent premier post!
Dennis

Pensez à utiliser -O3 -march=native. Votre CPU supporte lepopcnt instruction , et les compilateurs peuvent parfois reconnaître certaines de ses implémentations en C pur et compiler en une seule instruction. (Ou mieux, utilisez __builtin_popcountllGNU C, comme la réponse de Dennis).
Peter Cordes

-march=nativesur votre CPU Haswell activera également BMI2 pour des instructions de décalage à compte variable plus efficaces. ( SHLX au lieu de la SHL existante qui doit être comptabilisée cl.) Le processeur AMD Piledriver de l’OP n’a pas BMI2, mais il a un popcnt. Mais les processeurs AMD exécutent SHL à compte variable plus rapidement que les processeurs Intel. Compiler avec BMI2 lors du réglage peut donc toujours être approprié. Piledriver est assez différent de Haswell en ce qui concerne les micro-optimisations, mais il -march=nativeest bon de demander
Peter Cordes

12

Python 2 (PyPy 4.0), 2.36961s (29 février 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])

Ceci utilise la méthode de Meissel-Lehmer.

Les horaires

$ 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

Temps officiels

Puisqu'il y avait une autre réponse avec un temps similaire, j'ai opté pour des résultats plus précis. J'ai chronométré ceci 100 fois. Le score est le temps suivant divisé par 100.

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

5
Aussi, juste pour la note: ce code est 15.102.4 fois plus rapide que le mien. +1
Addison Crump

12

Java, 25,725,315 secondes sur cette machine

Cela ne va pas gagner , je voulais juste poster une réponse qui n'utilise pas de tamis.

MISE À JOUR: Ceci est actuellement classé à environ 150 440,4386 fois plus lent que le score principal. Allez-les voter, leur réponse est géniale.

Code d'octet:

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            ............

Code source:

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;
    }
}

Il s'avère que l'optimiseur augmentait en fait le temps nécessaire. >.> Dammit.

Une entrée en dessous de 1000 semble prendre un temps moyen de 0,157 sur mon ordinateur (probablement en raison du chargement de la classe ಠ_), mais après environ 1e7, cela devient difficile.

Liste de chronométrage:

> 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 est actuellement en cours d'exécution à un processeur cohérent à 100%. C'est totalement efficace, de quoi tu parles?
Addison Crump

pouvez-vous me donner un tutoriel sur la façon de java (car C / C ++> java). Je compile avec javac voteToClose.java(j'ai renommé la classe) et puis quoi?
Liam

@Liamjava voteToClose <input>
Addison Crump

1
Attendez ... Pourquoi le code d'octet dit-il cafe babe?
Cyoce

12
@Cyoce Tous les fichiers de classe Java sont dirigés avec 0xCAFEBABE.
Addison Crump

8

Rouille, 0.37001 sec (12 juin 2016)

Environ 10 fois plus lent que plus lent que la Créponse de Dennis , mais 10 fois plus rapide que son entrée en Python. Cette réponse est rendue possible par @Shepmaster et @Veedrac qui ont contribué à l’améliorer sur Code Review . Il est repris textuellement du post de @ 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
}

Timed with: time ./time.shtime.shressemble à:

#!/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;

Voici la sortie.

[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)

Nous avons finalement réussi à refaire cela, et c'est à la fois plus petit / plus simple et BEAUCOUP plus rapide qu'auparavant. Plutôt que d'utiliser une méthode de force brute plus lente, il utilise le tamis d'Eratosthenes parallèlement à des structures de données plus efficaces, de sorte qu'il est désormais capable de terminer dans un temps respectable (pour autant que je puisse trouver sur Internet, c'est le décompte JS prime le plus rapide fonctionner là-bas).

Quelques démos (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

Pourquoi +=1et pas ++?
ETHproductions

@ETHproductions Cela dépend si vous voulez dire pré ou post-incrémentation. i++doit conserver le changement de valeur pour une autre opération, ce qui, à cette échelle, entraîne un impact limité mais perceptible sur les performances. Je n'ai pas testé de pré-incrément, mais je suppose que ce sera à peu près le même que +=1.
Mwr247

Mais +=1doit allouer 1en mémoire. Je pense. Si j'étais vous, j'utiliserais ++i. Je pense qu'il y a une seule instruction pour incrémenter une valeur, alors je ne suis pas sûr.
Ismael Miguel

Pourquoi est-il si condensé? Ce n'est pas du code-golf , et c'est vraiment difficile à lire.
Cyoce

En outre, il pourrait être utile de passer (...)|0;i=0à(...)|(i=0)
Cyoce 27/02/2016

6

C ++ 11, 22.6503s (28 févr. 2016)

Compiler avec g++ -O2 -m64 -march=native -ftree-vectorize -std=c++11 numprimes.cpp. Ces options sont importantes. Vous devez également avoir installé Boost . Sur Ubuntu, cela est disponible en installant libboost-all-dev.

Si vous êtes sous Windows, je peux recommander l'installation g++et Boost via MSYS2 . J'ai écrit un bon tutoriel sur la façon d'installer MSYS2. Après avoir suivi le tutoriel, vous pouvez installer Boost avec 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;
}

Sur ma machine, cela s'exécute en 4,8 secondes pour 1907000000 (1.9e9).

Le code ci-dessus a été réutilisé à partir de ma bibliothèque personnelle C ++ , j'ai donc eu une longueur d'avance.

Temps officiels

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. C'est rapide. Quelle est votre machine?
Addison Crump

@VoteToClose Intel i5-4670k fonctionnant sous Windows 7 64 bits
orlp

Vous souhaitez ajouter une explication?
Liam

@ Liam C'est juste un tamis qui a un nombre quelconque qui est un multiple de 2 et 3 qui sont laissés hors du tamis.
Orlp

3

C ++, 2.47215s (29 février 2016)

Ceci est une version multitâche (négligée) de mon autre réponse.

#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';
}

Utilise un tamis segmenté d’Ératosthène avec une factorisation de roue de 6 pour éviter tous les multiples de 2/3. Utilise le POSIXffsll pour ignorer les valeurs composites consécutives.

Compiler:

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

timings non officiels

Timed avec un Intel i5-6600k sur Ubuntu 15.10, l'affaire 1907000000 a pris 0.817s.

Temps officiels

Pour obtenir des temps plus précis, j'ai chronométré ceci 100 fois, puis divisé le temps par 100.

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

Puisque ceci et la réponse en python de @Dennis sont si proches, je peux les recaler pour des résultats plus précis.
Liam

Waouh Waouh Waouh. Cela a encore moins de sens pour moi que CJam ou Pyth. Je l'appellerai le monstre à décalage! +1
Tamoghna Chowdhury

En passant, pourriez-vous essayer CUDA / OpenCL pour une accélération du GPU? Si j'en savais plus, je l'aurais peut-être.
Tamoghna Chowdhury

Oui, je suppose que j'étais un peu excessif avec le bithifting / masking: PI ne sait pas si GPGPU serait utile ici ou non; le seul domaine dans lequel je peux le voir aider est le pré-tamisage de petits nombres premiers, et même dans ce cas, les vitesses de transfert de données pourraient être suffisantes pour éliminer ce problème. Ce qui me choque encore, c'est que je suis toujours un facteur 10 ou plus de la mise en oeuvre du tamis
helloworld922

2

C, 2m42,7254s (28 février 2016)

Enregistrer sous pi.c, compiler sous gcc -o pi pi.c, exécuter sous ./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;
}

Nécessite beaucoup de mémoire pour fonctionner! Si votre matériel ne peut pas économiser jusqu'à deux gigaoctets de mémoire réelle, le programme se bloquera ou s'exécutera très lentement à cause des conflits VMM et HD.

Le temps approximatif sur mon matériel est 1,239 × 10 -8 · n 1,065 s. Par exemple, une entrée de n = 2 × 10 9 prend environ 100 s.

Temps officiels

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

Cela fonctionne avec le tamis d'eratosthenes? Je vais chronométrer quand je
Liam

Je commets une erreur sur le premier cas (les autres fonctionnent bien). Cela se produit après environ 1 minute d’exécution. J'ai ajouté la if (p==NULL) {exit(1);}ligne au code, donc je ne crois pas que le malloc échoue (il échouerait également au début, pas au bout d'une minute). Des idées sur ce qui se passe?
Liam

De nombreux systèmes, y compris Linux, font une allocation optimiste. Par exemple, si vous demandez 1 Go, il vous le "donnera", mais lorsque vous allez l'utiliser, et si le système ne le trouve pas, il se bloque. Si tel était le cas, cela risquerait de provoquer une panne du memset. La minute que cela prend est le temps passé à essayer de fusionner le tas en un bloc contigu. Vérifiez également que votre système est sizeof (bool) == 1. Si c'est == 4, je peux alors le réécrire pour qu'il utilise char.

J'ai déjà vérifié. Bool est un octet. Est-il possible de demander simplement au formulaire 2 * 10 ^ 9 octets de mémoire dans la pile? Déclarer une variable globale qui (sur gcc), je crois, sera initiée à 0. Cela nécessiterait d’utiliser charplutôt que je pense.
Liam

1
@Liam Difficile à dire. Le dépassement d'entier signé est un comportement indéfini. Par conséquent, sans regarder l'assemblage généré, il est difficile de prédire ce que le compilateur a fait.
Dennis

2

Julia, 1m 21.1329s

J'aimerais proposer quelque chose un peu plus rapidement, mais pour l'instant, voici une mise en œuvre plutôt naïve du tamis d'Eratosthène.

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))

Obtenez la dernière version de Julia pour votre système ici . Assurez-vous que l'exécutable Julia est sur votre chemin. Enregistrez le code sous sieve.jlet exécutez-le à partir de la ligne de commande julia sieve.jl N, où Nest l’entrée.

Temps officiels

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
J'ai implémenté le tamis d'Atkin et mon implémentation est plus lente. >: U
Alex A.

@ Liam Whoa. Je me demande pourquoi les temps officiels sont bien plus longs que les temps non officiels. Les temps officiels sont assez horribles.
Alex A.

Eh bien les temps officiels sont pour tous les cas de score ensemble. Les non officiels vont numéro par numéro. De plus, mon ordinateur n’est probablement pas aussi rapide que le vôtre.
Liam

@ Liam Oh, cela a plus de sens. Dang, je pensais que c'était correct. Eh bien, revenons à la planche à dessin.
Alex A.

Je suis sur le point de voler l'algorithme de Dennis ... juste pour comprendre à quelle vitesse il est.
Liam

2

Java, 42.663122s * (3 mars 2016)

* ceci a été chronométré en interne par le programme (sur l'ordinateur du PO cependant)

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);
}
}

Suit la grande tradition de code auto-documenté de PPCG (bien que pas au sens littéral: p).

Cela prouve que Java peut être assez rapide pour être compétitif par rapport à d’autres langages de machine virtuelle lorsqu’il utilise des algorithmes similaires.

Exécuter des informations

Exécutez-le comme vous auriez la réponse de @ CoolestVeto, mais le mien n'a pas besoin d'arguments de ligne de commande, il peut les obtenir de STDIN.

Modifiez la NUM_THREADSconstante pour la définir sur 2x votre nombre de cœurs natifs pour des performances maximales (comme je l’ai observé - dans mon cas, j’ai 8 cœurs virtuels, donc 16, l’opérateur voudra peut-être 12 pour son processeur hexa-core).

Lors de ces tests, j’utilisais JDK 1.7.0.45 avec BlueJ 3.1.6 (la mise à jour d’IntelliJ) sous Windows 10 Enterpise x64 sur un ordinateur portable ASUS K55VM (Core i7 3610QM, 8 Go de RAM). Google Chrome 49.0 64 bits avec 1 onglet (PPCG) ouvert et 1 fichier téléchargé par QBittorrent s'exécutaient en arrière-plan, avec 60% d'utilisation de la RAM au début de l'exécution.

Fondamentalement,

javac PrimeCounter.java
java PrimeCounter

Le programme vous guidera à travers le reste.

Le chronométrage est effectué par Java intégré System.nanoTime().

Détails de l'algorithme:

Dispose de 3 variantes pour différents cas d'utilisation - une version naïve comme celle de @ CoolestVeto (mais multithread) pour les entrées inférieures à 2 ^ 15, et un tamis masqué d'Eratosthenes avec élimination anormale pour des entrées supérieures à 2 ^ 28, et un tamis normal d'Eratosthenes avec Factorisation par roue 2/3/5/7 pour la pré-élimination des multiples.

J'utilise le tamis à masque de bits pour éviter des arguments JVM spéciaux pour les cas de test les plus volumineux. Si cela peut être fait, la surcharge pour le calcul du nombre dans la version masquée peut être éliminée.

Voici la sortie:

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

Afficher uniquement le résultat de pi (n) (sans invite) peut vous faire gagner un peu de temps, car STDOUT est ... eh bien, supposons que cela puisse être un peu plus rapide.
user48538

@ zyabin101, si quelqu'un avait la patience de consulter le code, il / elle comprendrait que la latence STDOUT a été prise en compte.
Tamoghna Chowdhury

Aussi pour le timing, j'ai envoyé stdout à / dev / null
Liam

@ Liam Je suppose que vous devrez faire une exception dans mon cas, alors. Vous pouvez modifier la méthode principale pour les arguments de ligne de commande, mais le programme est quand même automatique. Vérifiez quand même. S'il vous plaît?
Tamoghna Chowdhury

Bien sur. Je le ferai demain. Si j'ai du mal à vous parler, je vous envoie une requête ping
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)

Utilise le tamis d'Eratosthène. Fonctionne à une moyenne de 8.775sn = 10^7. À l'époque, j'ai utilisé la timecommande intégrée . Par exemple:

$ time python3 test.py 90
24

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

C'est le tamis! Je ne pouvais pas utiliser ceci en Java parce qu'il n'aimait pas la quantité de mémoire utilisée par un tableau booléen. D:
Addison Crump

erreur de mémoire sur les plus gros cas.
Liam

Quels cas? Je crois l'avoir corrigé. @Liam
Zach Gates

2
@VoteToClose Alors n'utilisez pas de tableau booléen. Utilisez un tableau entier et un décalage / masquage de bits, chaque bit représentant une valeur booléenne.
mbomb007

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

1

C ++, 9.3221s (29 févr. 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';
}

Utilise un tamis segmenté d’Ératosthène avec une factorisation de roue de 6 pour éviter tous les multiples de 2/3. Utilise POSIXffsll pour ignorer les valeurs composites consécutives.

Pourrait éventuellement être accéléré en faisant fonctionner le tamis segmenté en parallèle.

Compiler:

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

timings non officiels

Timed avec un Intel i5-6600k sur Ubuntu 15.10, l'affaire 1907000000 a pris 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

Horaires officiels

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.