Berechnen Sie die Anzahl der Primzahlen bis n


64

π ( n ) ist die Anzahl der Primzahlen kleiner oder gleich n .

Eingabe: eine natürliche Zahl, n .

Ausgabe: π (n).

Wertung: Dies ist eine Herausforderung mit dem . Score ist die Summe der Zeiten für die Score-Fälle. Ich werde jeden Eintrag auf meinem Computer zeitlich festlegen.

Regeln und Details

  • Ihr Code sollte für n bis zu 2 Milliarden (2.000.000.000) funktionieren .

  • Built-Ins, die dies trivialisieren, sind nicht erlaubt. Dies schließt eingebaute π-Funktionen oder Wertelisten für π ( n ) ein.

  • Built-Ins, die Primalität testen oder Primzahlen generieren, sind nicht zulässig. Dies schließt Listen von Primzahlen ein, die möglicherweise nicht extern nachgeschlagen oder lokal fest codiert werden, außer im Hinblick auf den nächsten Aufzählungspunkt.

  • Sie können Primzahlen bis einschließlich 19 und keine höheren fest codieren.

  • Ihre Implementierung von π sollte deterministisch sein. Dies bedeutet, dass bei einem bestimmten n Ihr Code (ungefähr) in der gleichen Zeit ausgeführt werden sollte.

  • Die verwendeten Sprachen müssen unter Linux (Centos 7) frei verfügbar sein. Anweisungen zum Ausführen des Codes sollten enthalten sein. Fügen Sie bei Bedarf die Compiler- / Interpreter-Details hinzu.

  • Die offiziellen Zeiten werden von meinem Computer sein.

  • Geben Sie beim Posten bitte eine selbst gemessene Zeit für einige / alle Test- / Score-Fälle an, um mir eine Schätzung der Geschwindigkeit Ihres Codes zu geben.

  • Einsendungen müssen in einen Antwortbeitrag zu dieser Frage passen.

  • Ich verwende 64bit centos7. Ich habe nur 8 GB RAM und 1 GB Swap. Das CPU-Modell ist: AMD FX (tm) -6300 Six-Core-Prozessor.

Testfälle ( Quelle ):

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

Score Cases ( gleiche Quelle )

Diese Fälle können sich wie gewohnt ändern. Eine Optimierung für die Bewertungsfälle ist nicht zulässig. Ich kann auch die Anzahl der Fälle ändern, um angemessene Laufzeiten und genaue Ergebnisse zu erzielen.

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

Dauer

Da dies eine Herausforderung mit dem und die Einträge auf meinem Computer ausgeführt werden sollen, behalte ich mir das Recht vor, die Zeiteinträge nach zwei Wochen zu beenden. Nach diesem Zeitpunkt werden noch Einträge akzeptiert, es kann jedoch nicht garantiert werden, dass sie offiziell terminiert sind.

Trotzdem erwarte ich nicht allzu viele Antworten auf diese Herausforderung und werde wahrscheinlich auf unbestimmte Zeit neue Antworten erhalten.

Bewertungsdetails

Ich habe die schnelleren Einträge mit dem folgenden Skript zeitlich festgelegt:

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

timeschreibt an stderr, also habe ich mit stderran eine log datei geschickt exec 2 >> <filename>. Möglicherweise stellen Sie fest, dass an stdoutgesendet wird /dev/null. Dies ist kein Problem, da ich bereits überprüft habe, ob die Programme die richtige Ausgabe produzieren.

Ich habe das obige timeall.shSkript 10 Mal mit ausgeführtfor i in {1..10}; do ./timeall.sh; done;

Ich habe dann die real timePunktzahl für jeden Eintrag gemittelt .

Beachten Sie, dass während des Timings keine anderen Programme auf meinem Computer ausgeführt wurden.

Außerdem wurden die offiziellen Zeiten an jeden Eintrag angehängt. Bitte überprüfen Sie Ihren eigenen Durchschnitt.


Was hindert uns daran, eine Nachschlagetabelle mit den ersten 2e9-Werten von pi (n) zu verwenden? Wäre das akzeptabel? (Nicht sicher, wie schnell es sein würde, weil es ein großer Tisch wäre)
Luis Mendo

@DonMuesli Das wäre nicht akzeptabel (widerspricht dem Geist der Herausforderung), ich habe es so bearbeitet, dass es jetzt auch ausdrücklich verboten ist.
Liam

8
Es ist gefährlich, sich auf den "Geist" der Herausforderung zu beziehen. Dein "gegen den Geist" ist vielleicht der "große Trick" eines anderen :-) Es ist besser, wenn du es explizit ausdrückst
Luis Mendo

1
Was ist ein eingebautes? Ich habe eine Liste von Primzahlen-Funktion in einer Bibliothek. Darf ich es benutzen? Wenn nicht, kann ich den Quellcode der Bibliothek in mein Programm kopieren und diesen verwenden?
nimi

1
@Liam: Ja, ich weiß, aber was zählt als eingebaut? Ist das Kopieren von Quellcode aus einer Bibliothek bereits integriert?
nimi

Antworten:


119

C, 0.026119s (12. März 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);
}

Dies erfolgt nach der Meissel-Lehmer-Methode .

Timings

Auf meinem Rechner erhalte ich für die kombinierten Testfälle ungefähr 5,7 Millisekunden . Dies ist auf einem Intel Core i7-3770 mit DDR3-RAM bei 1867 MHz, auf dem openSUSE 13.2 ausgeführt wird.

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

Da die Varianz zu hoch wurde , verwende ich programminterne Timings für die inoffiziellen Laufzeiten. Dies ist das Skript, das den Durchschnitt der kombinierten Laufzeiten berechnet hat.

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

Offizielle Zeiten

In dieser Zeit werden die Score-Fälle 1000-mal ausgeführt.

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

Wie es funktioniert

Formel

Sei eine positive ganze Zahl.x

Jede positive ganze Zahl erfüllt genau eine der folgenden Bedingungen.nx

  1. n=1

  2. n ist teilbar durch eine Primzahl in [ 1 , 3 p.[1,x3]

  3. , wobei p und q (nicht notwendigerweise verschiedene) Primzahlen in ( 3 √ sindn=pqpq.(x3,x23)

  4. ist prim und n > 3 nn>x3

Sei die Anzahl der Primzahlen p, so dass p y ist . Es gibt π ( x ) - π ( 3 π(y)ppyZahlen, die in die vierte Kategorie fallen.π(x)π(x3)

Sei die Menge der positiven ganzen Zahlen m y , die ein Produkt von genau k Primzahlen sind, die nicht zu den ersten c Primzahlen gehören. Es gibt P 2 ( x , π ( 3 Pk(y,c)mykcZahlen, die in die dritte Kategorie fallen.P2(x,π(x3))

Schließlich sei die Anzahl der positiven ganzen Zahlen k y , die zu den ersten c Primzahlen koprime sind. Es gibt x - ϕ ( x , π ( 3 ϕ(y,c)kycZahlen, die in die zweite Kategorie fallen.xϕ(x,π(x3))

Da es in allen Kategorien Zahlen gibt ,x

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

und deshalb,

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

Die Zahlen in der dritten Kategorie haben eine eindeutige Darstellung, wenn wir und daher p benötigenpq . Auf diese Weise fällt das Produkt der Primzahlenpundqgenau dann in die dritte Kategorie, wenn 3 pxpq , also gibt esπ(xx3<pqxpmögliche Werte fürqfür einen festen Wert vonpundP2(x,π(3√)π(xp)π(p)+1qp, wobeipkdiek-tePrimzahl bezeichnet.P2(x,π(x3))=π(x3)<kπ(x)(π(xpk)π(pk)+1)pkkth

Schließlich jede positive ganze Zahl das ist nicht auf die ersten Koprimzahlen c Primzahlen kann in einzigartiger Art und Weise ausgedrückt werden als n = p k f , wobei p k die niedrigste Primfaktor ist n . Auf diese Weise ist k c und f gleichzeitig mit den ersten k - 1 Primzahlen.nycn=pkfpknkcfk1

Dies führt zu der rekursiven Formel . Insbesondere ist die Summe leer, wennc=0 ist, alsoϕ(y,0)=y.ϕ(y,c)=y1kcϕ(ypk,k1)c=0ϕ(y,0)=y

Wir haben jetzt eine Formel, mit der wir berechnen können, indem wir nur das erste π ( 3 ) erzeugenπ(x)Primzahlen (Millionen gegen Milliarden).π(x23)

Algorithmus

Wir müssen π ( x berechnen, wobeipnur3√ betragen kannπ(xp)p . Während es andere Möglichkeiten gibt, dies zu tun (wie das rekursive Anwenden unserer Formel), scheint der schnellste Weg darin zu bestehen, alle Primzahlen bis zu3 √ aufzulistenx3 , was mit dem Sieb des Eratosthenes gemacht werden kann.x23

Zunächst identifizieren und speichern wir alle Primzahlen in und berechneπ( 3 [1,x]undπ(π(x3)gleichzeitig. Dann berechnen wir xπ(x) für allekin(π(3xpkkund zähle die Primzahlen bis zu jedem nachfolgenden Quotienten.(π(x3),π(x)]

Auch hat die geschlossene Formπ( 3 π(x3)<kπ(x)(π(pk)+1) , wodurch wir die Berechnung vonP2(x,π(3√)vervollständigen könnenπ(x3)π(x))(π(x3)+π(x)12.P2(x,π(x3))

Damit bleibt die Berechnung von , dem teuersten Teil des Algorithmus. Die einfache Verwendung der rekursiven Formel würde 2 c- Funktionsaufrufe erfordern , um ϕ ( y , c ) zu berechnen .ϕ2cϕ(y,c)

Zu allererst für alle Werte von c , so φ ( y , c ) = y - Σ 1 k c , p ky φ ( yϕ(0,c)=0c. Diese Beobachtung allein reicht bereits aus, um die Berechnung durchführbar zu machen. Dies liegt daran, dass jede Zahl unter2109kleiner ist als das Produkt von zehn verschiedenen Primzahlen, sodass die überwiegende Mehrheit der Summanden verschwindet.ϕ(y,c)=y1kc,pkyϕ(ypk,k1)2109

Durch Gruppieren von und den ersten c' - Summanden der Definition von ϕ erhalten wir auch die alternative Formel ϕ ( y , c ) = ϕ ( y , c ' ) - c ' < k c , p ky ϕ ( yycϕ. Somit werden durch Vorberechnen vonϕ(y,c')für ein festesc'und geeignete Werte vony diemeisten verbleibenden Funktionsaufrufe und die zugehörigen Berechnungen gespart.ϕ(y,c)=ϕ(y,c)c<kc,pkyϕ(ypk,k1)ϕ(y,c)cy

Wenn , dann ist ϕ ( m c , c ) = φ ( m c ) , da die ganzen Zahlen in [ 1 , m c ] durch keines von p 1 , , p teilbar sind c sind genau diejenigen, die zu m c coprime sind . Auch da gcd ( z + m c , mmc=1kcpkϕ(mc,c)=φ(mc)[1,mc]p1,,pcmc , wir haben das ϕ ( y , c ) = ϕ ( ygcd(z+mc,mc)=gcd(z,mc).ϕ(y,c)=ϕ(ymcmc,c)+ϕ(y

Da die Euler'sche Totientenfunktion multiplikativ ist, ist , und wir haben einen einfachen Weg, ϕ ( y , c ) für alle y durch Vorausberechnung der Werte nur für die y in [ 0 , m c ) .φ(mc)=1kcφ(pk)=1kc(pk1)ϕ(y,c)yy[0,mc)

Wenn wir , erhalten wir auch ϕ ( y , c ) = ϕ ( y , c - 1 ) - ϕ ( yc=c1, die ursprüngliche Definition aus Lehmers Arbeit. Dies gibt uns eine einfache Möglichkeit,ϕ(y,c)vorab zu berechnen,umdie Werte vonczu erhöhen.ϕ(y,c)=ϕ(y,c1)ϕ(ypc,c1)ϕ(y,c)c

Zusätzlich zum Vorberechnen von für einen bestimmten, niedrigen Wert von c berechnen wir ihn auch für niedrige Werte von y vor und kürzen die Rekursion, nachdem ein bestimmter Schwellenwert unterschritten wurde.ϕ(y,c)cy

Implementierung

Der vorherige Abschnitt behandelt die meisten Teile des Codes. Ein verbleibendes wichtiges Detail ist, wie die Unterteilungen in der Funktion Phidurchgeführt werden.

Da für die Berechnung von nur das erste π ( 3 √) dividiert werden mussϕPrimzahlen, wir könnenstattdessendieFunktion verwenden. Anstatt einfach einydurch eine Primzahlp zudividieren, multiplizieren wirymitdp 2 64π(x3)fastdivypy stattdessen und erholenydp264p alsdpyyp . Aufgrund der Implementierung der ganzzahligen Multiplikation inx64ist das Teilen durch264nicht erforderlich. die höheren 64 Bits vondpywerden in ihrem eigenen Register gespeichert.dpy264264dpy

Beachten Sie, dass für diese Methode die Vorberechnung von erforderlich ist , was nicht schneller ist als die Berechnung von ydp direkt. Da wir uns jedoch immer wieder durch dieselben Primzahlen teilen müssen und die Teilungso viel langsamerals die Multiplikation ist, führt dies zu einer bedeutenden Beschleunigung. Weitere Details zu diesem Algorithmus sowie einen formalen Beweis finden Sie inDivision durch invariante ganze Zahlen mit Multiplikation.yp


22
Man übertrifft Dennis nicht einfach?
Addison Crump

8
Ehrlich gesagt kann ich einfach nicht glauben, wie schnell das ist. Ich hatte nicht die Zeit durchzugehen und zu verstehen, was los ist, aber ich muss es wirklich.
Liam

27
@Liam Ich habe die Absicht zu erklären, wie das funktioniert, aber ich versuche immer noch, es zu beschleunigen. Im Moment wünsche ich mir wirklich, dass PPCG LaTeX hat ...
Dennis

15
Hinweis zum Spaß: (Auf meinem Computer) Dies schlägt derzeit sowohl Mathematicas eingebaute als auch die kimwalische Primecount-C ++ - Bibliothek von Github. Dies ist jedoch derzeit der einzige Eintrag, der dies tut.
Michael Klein

10
@TheNumberOne shh erzähle ihm nichts davon ... andere Leute könnten das brauchen, um ihn zu schlagen
Liam

24

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

Eine bitmapbasierte Siebe-of-Erastothenes-Implementierung. Es führt die folgenden Schritte aus:

  1. Erzeugen Sie zunächst ein sich wiederholendes Bitmuster, mit dem das Sieb gefüllt wird und das ein Vielfaches von 2,3,5,7 abdeckt
  2. Verwenden Sie als Nächstes die Siebmethode, um ein Array aller Primzahlen zu generieren, die kleiner als sqrt (n) sind.
  3. Verwenden Sie als Nächstes die Primliste aus dem vorherigen Schritt, um in das Sieb zu schreiben. Dies geschieht an Teilen des Siebs, die ungefähr die Größe eines L1-Caches haben, so dass die Siebverarbeitung den L1-Cache nicht ständig zerschlägt. dies scheint zu einer 5-fachen Beschleunigung zu führen, wenn kein Chunking erfolgt.
  4. Führen Sie abschließend eine Bitzählung durch.

Kompiliert mit gcc primecount.c -O3 -lm -Wallund ausgeführt auf Ubuntu 15.10 (64-Bit) auf einem i7-4970k dauert es ungefähr 2,2 Sekunden, bis der gesamte Satz der Score-Fälle vorliegt. Die Laufzeit wird von Schritt 3 dominiert; Dies kann auf Wunsch mit mehreren Threads erfolgen, da die Chunks unabhängig voneinander sind. Dies würde einige Sorgfalt erfordern, um sicherzustellen, dass die Blockgrenzen angemessen ausgerichtet sind.

Es weist etwas mehr Speicher zu, als für das Sieb unbedingt erforderlich ist. Dadurch wird Platz für einen Pufferende-Überlauf geschaffen, der erforderlich ist, damit die in Schritt 3 ausrollende Schleife ordnungsgemäß funktioniert.

Offizielle Zeiten

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
Willkommen bei Programming Puzzles & Code Golf und herzlichen Glückwunsch zu einem hervorragenden ersten Beitrag!
Dennis

Erwägen Sie die Verwendung -O3 -march=native. Ihre CPU unterstützt die popcntAnweisung , und Compiler können manchmal einige reine C-Implementierungen erkennen und zu der einzelnen Anweisung kompilieren. (Oder besser, __builtin_popcountllauf GNU C verwenden, wie Dennis Antwort).
Peter Cordes

-march=nativeAuf Ihrer Haswell-CPU wird BMI2 auch für effizientere Anweisungen zur Verschiebung mit variabler Anzahl aktiviert. ( SHLX anstelle der alten SHL , die berücksichtigt werden muss cl.) Die AMD Piledriver-CPU des OP verfügt nicht über BMI2, aber über Popcnt. Da AMD-CPUs SHL mit variabler Anzahl schneller ausführen als Intel-CPUs, ist das Kompilieren mit BMI2 während der Optimierung möglicherweise immer noch sinnvoll. Piledriver unterscheidet sich von Haswell in -march=native
Peter Cordes,

12

Python 2 (PyPy 4.0), 2.36961s (29. Februar 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])

Dies erfolgt nach der Meissel-Lehmer-Methode.

Timings

$ 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

Offizielle Zeiten

Da es eine andere Antwort mit einer ähnlichen Zeit gab, entschied ich mich für genauere Ergebnisse. Ich habe das 100 Mal gemacht. Die Punktzahl ist die folgende Zeit geteilt durch 100.

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

5
Nur zur Erinnerung: Dieser Code ist 15.102,4-mal schneller als meiner. +1
Addison Crump

12

Java, 25,725.315 Sekunden auf diesem Computer

Dies wird nicht gewinnen , ich wollte nur eine Antwort posten, die keine Siebe verwendet.

UPDATE: Dieser Wert ist derzeit etwa 150.440.4386-mal langsamer als der führende Wert. Stimmen Sie ab, ihre Antwort ist großartig.

Byte-Code:

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

Quellcode:

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

Es stellte sich heraus, dass der Optimierer tatsächlich die benötigte Zeit verlängerte. >.> Verdammt.

Eingaben unter 1000 scheinen auf meinem Computer durchschnittlich 0,157 Sekunden zu dauern (wahrscheinlich aufgrund des Ladens der Klasse ಠ_ಠ), aber nach etwa 1e7 wird es pingelig.

Timing-Liste:

> 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 läuft derzeit mit einer konsistenten 100% igen CPU. Das ist total effizient, wovon redest du?
Addison Crump

Kannst du mir ein Tutorial geben, wie man Java beendet (weil C / C ++> Java)? Ich kompiliere mit javac voteToClose.java(ich habe die Klasse umbenannt) und was dann?
Liam

@ Liamjava voteToClose <input>
Addison Crump

1
Warten Sie ... Warum sagt der Byte-Code cafe babe?
Cyoce

12
@Cyoce Alle Java-Klassendateien haben die Überschrift 0xCAFEBABE.
Addison Crump

8

Rust, 0,37001 Sek. (12. Juni 2016)

Etwa zehnmal langsamer als Dennis ' CAntwort, aber zehnmal schneller als sein Python-Eintrag. Diese Antwort wird von @Shepmaster und @Veedrac ermöglicht, die bei der Verbesserung von Code Review geholfen haben . Es ist wörtlich von @ Veedrac's Post übernommen .

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
}

Zeitlich festgelegt mit: time ./time.shwo time.shsieht es aus wie:

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

Hier ist die Ausgabe.

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

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

8

Node.js (JavaScript / ES6), 83.549s (11. November 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)

Endlich habe ich mich darum gekümmert, dies zu überarbeiten, und es ist sowohl kleiner / einfacher als auch VIEL schneller als zuvor. Anstatt einer langsameren Brute-Force-Methode wird das Sieb des Eratosthenes zusammen mit effizienteren Datenstrukturen verwendet, sodass es jetzt in einer respektablen Zeit fertig werden kann (soweit ich im Internet finde, ist es die schnellste JS-Primzahl) Funktion da draußen).

Einige Demo-Zeiten (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

Warum +=1nicht ++?
ETHproductions

@ETHproductions Kommt darauf an, ob du Pre- oder Post-Increment meinst. i++muss die Wertänderung für eine andere Operation halten, was in dieser Größenordnung zu einem kleinen, aber spürbaren Leistungseinbruch führt. Ich habe das Pre-Increment nicht getestet, aber ich vermute, es wird ungefähr so ​​sein wie +=1.
Mwr247

Aber +=1muss zuzuteilen 1in den Speicher. Ich denke. Wenn ich du wäre, würde ich verwenden ++i. Ich denke, dass es eine einzige Anweisung gibt, um einen Wert zu erhöhen, also bin ich mir nicht sicher.
Ismael Miguel

Warum ist es so kondensiert? Dies ist kein Code-Golf , und das ist wirklich schwer zu lesen.
Cyoce

Es könnte auch hilfreich sein, zu wechseln (...)|0;i=0zu(...)|(i=0)
Cyoce

6

C ++ 11, 22.6503s (28. Februar 2016)

Kompilieren mit g++ -O2 -m64 -march=native -ftree-vectorize -std=c++11 numprimes.cpp. Diese Optionen sind wichtig. Sie müssen auch Boost installiert haben. Unter Ubuntu ist dies durch die Installation möglich libboost-all-dev.

Wenn Sie unter Windows arbeiten, kann ich die Installation g++und das Boosten über MSYS2 empfehlen . Ich habe ein nettes Tutorial geschrieben, wie man MSYS2 installiert. Nachdem Sie dem Tutorial gefolgt sind, können Sie Boost mit installieren 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;
}

Auf meinem Computer läuft dies in 4,8 Sekunden für 1907000000 (1.9e9).

Der obige Code wurde aus meiner persönlichen C ++ - Bibliothek umfunktioniert , sodass ich einen Vorsprung hatte.

Offizielle Zeiten

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. Das geht schnell Was ist deine Maschine?
Addison Crump

@VoteToClose Intel i5-4670k mit 64-Bit-Windows 7.
orlp

Möchtest du eine Erklärung hinzufügen?
Liam

@ Liam Es ist nur ein Sieb mit einer beliebigen Zahl, die ein Vielfaches von 2 und 3 ist und nicht im Sieb enthalten ist.
Orlp

3

C ++, 2.47215s (29. Februar 2016)

Dies ist eine (schlampige) Multithread-Version meiner anderen Antwort.

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

Verwendet ein segmentiertes Sieb aus Eratosthenes mit einer Radfaktorisierung von 6, um alle Vielfachen von 2/3 zu überspringen. Verwendet das POSIX ffsll, um aufeinanderfolgende zusammengesetzte Werte zu überspringen.

Kompilieren:

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

inoffizielle Zeiten

Zeitlich abgestimmt mit einem Intel i5-6600k auf Ubuntu 15.10, dauerte der Fall 1907000000 0.817s.

Offizielle Zeiten

Um genauere Zeiten zu erhalten, habe ich diese 100-mal getimt und dann die Zeit durch 100 geteilt.

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

Da dies und @Dennis 's Python-Antwort so nah beieinander liegen, kann ich sie für genauere Ergebnisse neu einplanen.
Liam

Wow wow wow. Das macht für mich noch weniger Sinn als CJam oder Pyth. Ich werde es das Bit-Shift-Monster nennen! +1
Tamoghna Chowdhury

Könnten Sie nebenbei CUDA / OpenCL für eine GPU-Beschleunigung ausprobieren? Wenn ich mehr C wüsste, hätte ich vielleicht.
Tamoghna Chowdhury

Ja, ich schätze, ich war ein bisschen übertrieben mit Bitverschiebung / Maskierung: PI weiß nicht, ob GPGPU hier hilfreich wäre oder nicht; der einzige Bereich, in dem ich sehe, dass es möglicherweise hilft, ist das Vorsieben kleiner Primzahlen, und selbst dann könnten die Datenübertragungsgeschwindigkeiten ausreichen, um dies zu beseitigen. Was mich immer noch stört, ist, dass ich immer noch um den Faktor 10 von der schnellsten
Siebimplementierung abweiche, die

2

C, 2M42.7254s (28. Februar 2016)

Speichern als pi.c, Kompilieren als gcc -o pi pi.c, Ausführen als ./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;
}

Benötigt viel Speicher zum Laufen! Wenn Ihre Hardware nicht bis zu zwei Gigabyte realen Arbeitsspeichers sparen kann, stürzt das Programm aufgrund von VMM- und HD-Thrashing entweder ab oder wird sehr langsam ausgeführt.

Das ungefähre Timing meiner Hardware beträgt 1,239 × 10 -8 · n 1,065 s. Zum Beispiel dauert eine Eingabe von n = 2 × 10 9 ungefähr 100 s, bis sie ausgeführt wird.

Offizielle Zeiten

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

Dies funktioniert mit dem Sieb von Eratosthenen? Ich werde es mal sehen, wenn ich nach Hause komme
Liam

Ich habe im ersten Fall einen Fehler gemacht (andere laufen gut). Dies geschieht nach ca. 1 Minute Laufzeit. Ich if (p==NULL) {exit(1);}habe dem Code eine Zeile hinzugefügt , sodass ich nicht glaube, dass das Malloc ausfällt (es würde auch am Anfang ausfallen, nicht 1 Minute später). Ideen, was passiert?
Liam

Viele Systeme, einschließlich Linux, weisen optimistisch zu. Wenn Sie beispielsweise nach 1 GB fragen, wird es Ihnen "gegeben", aber wenn Sie es tatsächlich verwenden, und wenn das System es nicht finden kann, stürzt es ab. Wenn dies der Fall wäre, würde es wahrscheinlich am memset abstürzen. Die Minute, die es dauert, ist der Zeitaufwand für den Versuch, den Haufen zu einem zusammenhängenden Block zusammenzufassen. Überprüfen Sie auch, ob Ihr System sizeof (bool) == 1 ist. Wenn es == 4 ist, kann ich dies umschreiben, um char zu verwenden.

Ich habe schon nachgesehen. Bool ist 1 Byte. Ist es möglich, nur 2 * 10 ^ 9 Bytes Speicher im Stapel abzufragen? Dh deklariere eine globale Variable, von der ich glaube, dass sie (auf gcc) auf 0 initialisiert wird. Dies würde charstattdessen die Verwendung erfordern, obwohl ich denke.
Liam

1
@ Liam Schwer zu sagen. Ein vorzeichenbehafteter Ganzzahlüberlauf ist ein undefiniertes Verhalten. Ohne die generierte Assembly zu betrachten, ist es schwierig, vorherzusagen, was der Compiler getan hat.
Dennis

2

Julia, 1m 21.1329s

Ich würde gerne etwas schneller einfallen lassen, aber im Moment ist hier eine ziemlich naive Implementierung des Siebes von Eratosthenes.

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

Die neueste Version von Julia für Ihr System hier . Stellen Sie sicher, dass sich die ausführbare Julia-Datei in Ihrem Pfad befindet. Speichern Sie den Code unter sieve.jlund führen Sie ihn über die Befehlszeile aus julia sieve.jl N. Dort Nbefindet sich die Eingabe.

Offizielle Zeiten

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
Ich habe das Sieb von Atkin implementiert und meine Implementierung dafür ist langsamer. >: U
Alex A.

@ Liam Whoa. Ich frage mich, warum die offiziellen Zeiten viel länger sind als meine inoffiziellen. Die offiziellen Zeiten sind ziemlich schrecklich.
Alex A.

Nun, die offiziellen Zeiten sind für alle Fälle zusammen. Die inoffiziellen gehen Nummer für Nummer. Außerdem ist mein Computer wahrscheinlich nicht so schnell wie deins.
Liam

@Liam Oh, das macht mehr Sinn. Verdammt, ich fand das anständig. Na ja, zurück zum Zeichenbrett.
Alex A.

Ich werde Dennis 'Algorithmus stehlen ... nur damit ich verstehen kann, wie schnell es ist.
Liam

2

Java, 42,663122s * (3. März 2016)

* Dies wurde intern vom Programm gesteuert (allerdings auf dem OP-Computer)

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

Folgt der großen PPCG-Tradition des selbstdokumentierenden Codes (wenn auch nicht im wörtlichen Sinne: p).

Dies soll beweisen, dass Java schnell genug sein kann, um mit ähnlichen Algorithmen mit anderen VM-Sprachen konkurrieren zu können.

Informationen ausführen

Führe es so aus, als hättest du @ CoolestVetos Antwort, aber meine benötigt keine Befehlszeilenargumente, sie können von STDIN bezogen werden.

Passen Sie die NUM_THREADSKonstante an, um die maximale Leistung zu erzielen, indem Sie sie auf das Zweifache der Anzahl Ihrer nativen Kerne einstellen (wie ich festgestellt habe - In meinem Fall habe ich 8 virtuelle Kerne, also ist sie auf 16 festgelegt. Das OP möchte möglicherweise 12 für seinen Hexa-Core-Prozessor).

Als ich diese Tests durchführte, verwendete ich JDK 1.7.0.45 mit BlueJ 3.1.6 (IntelliJ wurde aktualisiert) unter Windows 10 Enterpise x64 auf einem ASUS K55VM-Laptop (Core i7 3610QM, 8 GB RAM). Google Chrome 49.0 64-Bit mit 1 geöffneten Tab (PPCG) und QBittorrent, das 1 Datei herunterlädt, wurde im Hintergrund ausgeführt, 60% RAM-Auslastung zu Beginn der Ausführung.

Grundsätzlich gilt,

javac PrimeCounter.java
java PrimeCounter

Das Programm führt Sie durch den Rest.

Das Timing wird von Java eingebaut System.nanoTime().

Algorithmus Details:

Hat 3 Varianten für verschiedene Anwendungsfälle - eine naive Version wie @ CoolestVeto (aber Multithread) für Eingaben unter 2 ^ 15 und ein bitmaskiertes Eratosthenes-Sieb mit ungerader Eliminierung für Eingaben über 2 ^ 28 und ein normales Eratosthenes-Sieb mit a Radfaktorisierung 2/3/5/7 zur Vorab-Eliminierung von Vielfachen.

Ich verwende das Bitmasken-Sieb, um spezielle JVM-Argumente für die größten Testfälle zu vermeiden. Wenn dies möglich ist, kann der Aufwand für die Berechnung der Zählung in der bitmaskierten Version entfallen.

Hier ist die Ausgabe:

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

Die Ausgabe nur des Ergebnisses von pi (n) (ohne Eingabeaufforderungen) kann einige Zeit sparen, da STDOUT ... naja, sagen wir, es könnte einfach etwas schneller sein.
user48538

@ zyabin101, wenn jemand die Geduld hätte, den Code durchzugehen, würde er / sie verstehen, dass die STDOUT-Latenz berücksichtigt wurde.
Tamoghna Chowdhury

Auch für das Timing habe ich stdout an / dev / null
Liam

@ Liam Dann musst du in meinem Fall wohl eine Ausnahme machen. Sie können die Hauptmethode für Befehlszeilenargumente anpassen, das Programm führt jedoch trotzdem eine Selbststeuerung durch. Probieren Sie es trotzdem aus. Bitte?
Tamoghna Chowdhury

Natürlich werde ich. Ich mache es morgen. Wenn ich Probleme habe, rufe ich Sie im Chat an
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)

Verwendet das Sieb von Eratosthenes. Läuft mit einem Durchschnitt von 8.775swo n = 10^7. Bisher habe ich den eingebauten timeBefehl verwendet. Zum Beispiel:

$ time python3 test.py 90
24

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

Es ist das Sieb! Ich konnte dies in Java nicht verwenden, da es nicht gefallen hat, wie viel Speicher ein boolesches Array verwendet. D:
Addison Crump

Speicherfehler bei den größeren Fällen.
Liam

Welche Fälle? Ich glaube, ich habe es behoben. @ Liam
Zach Gates

2
@VoteToClose Dann verwenden Sie kein Boolesches Array. Verwenden Sie ein Integer-Array und eine Bitverschiebung / -maskierung, wobei jedes Bit einen Booleschen Wert darstellt.
mbomb007

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

1

C ++, 9,3221s (29. Februar 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';
}

Verwendet ein segmentiertes Sieb aus Eratosthenes mit einer Radfaktorisierung von 6, um alle Vielfachen von 2/3 zu überspringen. Verwendet das POSIX ffsll, um aufeinanderfolgende zusammengesetzte Werte zu überspringen.

Könnte möglicherweise beschleunigt werden, indem das segmentierte Sieb parallel arbeitet.

Kompilieren:

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

inoffizielle Zeiten

Zeitlich abgestimmt mit einem Intel i5-6600k auf Ubuntu 15.10, dauerte der Fall 1907000000 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

Offizielle Zeiten

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.