Hitung jumlah bilangan prima hingga n


64

π ( n ) adalah jumlah bilangan prima kurang dari atau sama dengan n .

Input: angka alami, n .

Output: π (n).

Penilaian: Ini adalah tantangan . Skor akan menjadi jumlah kali untuk kasus skor. Saya akan mengatur waktu setiap entri di komputer saya.

Aturan dan Detail

  • Kode Anda harus bekerja untuk n hingga 2 miliar (2.000.000.000).

  • Built-in yang meremehkan ini tidak diperbolehkan. Ini termasuk fungsi π built atau daftar nilai untuk π ( n ).

  • Built-in yang menguji keutamaan atau menghasilkan bilangan prima tidak diizinkan. Ini termasuk daftar bilangan prima, yang mungkin tidak dapat dilihat secara eksternal atau dikodekan secara lokal, kecuali berkaitan dengan poin-poin berikutnya.

  • Anda dapat memasang hardcode hingga dan termasuk 19 dan tidak lebih tinggi.

  • implementasi Anda dari π harus bersifat deterministik. Ini berarti bahwa dengan n tertentu , kode Anda harus berjalan dalam (kurang-lebih) jumlah waktu yang sama.

  • Bahasa yang digunakan harus tersedia secara bebas di Linux (Centos 7). Instruksi harus disertakan tentang cara menjalankan kode Anda. Sertakan detail kompilator / juru bahasa jika perlu.

  • Waktu resmi akan dari komputer saya.

  • Saat memposting, harap sertakan waktu pengukuran sendiri pada beberapa / semua kasus tes / skor, hanya untuk memberi saya perkiraan seberapa cepat kode Anda berjalan.

  • Kiriman harus sesuai dengan pos jawaban untuk pertanyaan ini.

  • Saya menjalankan 64bit centos7. Saya hanya memiliki 8GB RAM dan 1GB swap. Model cpu adalah: AMD FX (tm) -6300 Six-Core Processor.

Kasus uji ( sumber ):

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

Angka Kasus ( sumber yang sama )

Seperti biasa, kasus-kasus ini dapat berubah. Mengoptimalkan untuk kasus pemberian skor tidak diizinkan. Saya juga dapat mengubah jumlah kasus dalam upaya untuk menyeimbangkan waktu menjalankan yang wajar dan hasil yang akurat.

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

Durasi

Karena ini adalah tantangan dan entri harus dijalankan di komputer saya, saya berhak untuk menghentikan entri waktu setelah 2 minggu. Setelah titik ini, entri masih diterima, tetapi tidak ada jaminan bahwa mereka secara resmi diatur waktunya.

Setelah mengatakan ini, saya tidak berharap terlalu banyak jawaban untuk tantangan ini dan saya kemungkinan akan terus menghitung waktu jawaban baru tanpa batas.

Mencetak Skor

Saya menghitung entri yang lebih cepat dengan skrip berikut:

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

timemenulis ke stderr, jadi saya mengirim stderrke file log menggunakan exec 2 >> <filename>. Anda mungkin memperhatikan bahwa stdoutdikirim ke /dev/null. Ini bukan masalah, karena saya sudah memverifikasi bahwa program menghasilkan output yang benar.

Saya menjalankan timeall.shscript di atas 10 kali menggunakanfor i in {1..10}; do ./timeall.sh; done;

Saya kemudian rata-rata real timeskor untuk setiap entri.

Perhatikan bahwa tidak ada program lain yang berjalan di komputer saya saat penghitungan waktu.

Juga, waktu resmi telah ditambahkan ke setiap entri. Periksa ulang rata-rata Anda sendiri.


Apa yang mencegah kita menggunakan tabel pencarian dengan nilai 2e9 pertama dari pi (n)? Apakah itu bisa diterima? (Tidak yakin seberapa cepat itu, karena itu akan menjadi meja besar)
Luis Mendo

@ DonMuesli Itu tidak akan diterima (bertentangan dengan semangat tantangan), saya telah mengeditnya untuk membuatnya secara tegas terlarang juga sekarang.
Liam

8
Berbahaya untuk merujuk pada "semangat" tantangan. "Melawan semangat" Anda mungkin adalah "trik hebat" orang lain :-) Lebih baik Anda membuatnya eksplisit
Luis Mendo

1
Apa itu built-in? Saya memiliki fungsi daftar prima di perpustakaan. Bolehkah saya menggunakannya? Jika tidak, dapatkah saya menyalin kode sumber perpustakaan di program saya dan menggunakannya?
nimi

1
@Liam: Ya, saya tahu, tetapi apa yang dianggap sebagai built-in? Apakah menyalin kode sumber dari perpustakaan adalah bawaan?
nimi

Jawaban:


119

C, 0,026119s (12 Mar 2016)

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

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

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

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

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

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

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

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

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

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

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

    return r;
}

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

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

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

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

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

    return r;
}

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

    return 0;
}

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

    clock_gettime(CLOCK_REALTIME, &then);

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

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

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

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

        sieve[i] = ~mask;
    }

    limit = min(halftop, 8 * cache_size);

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

    a = t;

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

    b = t;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ini menggunakan metode Meissel-Lehmer .

Pengaturan waktu

Di mesin saya, saya mendapatkan sekitar 5,7 milidetik untuk kasus uji gabungan. Ini menggunakan Intel Core i7-3770 dengan RAM DDR3 pada 1867 MHz, menjalankan 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

Karena varians menjadi terlalu tinggi , saya menggunakan timing dari dalam program untuk waktu berjalan tidak resmi. Ini adalah skrip yang menghitung rata-rata waktu menjalankan gabungan.

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

Waktu resmi

Kali ini adalah untuk melakukan skor kasus 1000 kali.

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

Bagaimana itu bekerja

Rumus

Biarkan menjadi bilangan bulat positif.x

Setiap bilangan bulat positif memenuhi persis salah satu dari kondisi berikut.nx

  1. n=1

  2. dapat dibagi dengan bilangan prima p dalam [ 1 , 3 nhal.[1,x3]

  3. , di mana p dan q adalah (tidak harus berbeda) bilangan prima dalam ( 3 n=halqhalq.(x3,x23)

  4. adalah prima dan n > 3 nn>x3

Misalkan menunjukkan jumlah bilangan prima p sehingga p y . Ada π ( x ) - π ( 3 π(y)halhalyangka yang termasuk dalam kategori keempat.π(x)-π(x3)

Misalkan menunjukkan jumlah bilangan bulat positif m y yang merupakan produk dari bilangan prima k yang tepat bukan di antara bilangan prima c pertama . Ada P 2 ( x , π ( 3 Pk(y,c)mykcangka yang termasuk dalam kategori ketiga.P2(x,π(x3))

Akhirnya, misalkan menunjukkan jumlah bilangan bulat positif k y yang merupakan coprime ke bilangan prima c pertama . Ada x - ϕ ( x , π ( 3 ϕ(y,c)kycxϕ(x,π(x3)) angka yang termasuk dalam kategori kedua.

Karena ada angka di semua kategori,x

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

dan maka dari itu,

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

Angka-angka dalam kategori ketiga memiliki representasi unik jika kita memerlukan dan, oleh karena itu p pq . Dengan cara ini, produk bilangan primapdanqberada dalam kategori ketiga jika dan hanya jika 3 pxpq , jadi adaπ(xx3<pqxpnilai yang mungkin untukquntuk nilai tetapp, danP2(x,π(3π(xp)π(p)+1qp, di manapkmenunjukkankthP2(x,π(x3))=π(x3)<kπ(x)(π(xpk)π(pk)+1)pkkth bilangan prima.

Akhirnya, setiap bilangan bulat positif yang tidak coprime untuk pertama c bilangan prima dapat dinyatakan dalam cara yang unik sebagai n = p k f , di mana p k adalah faktor utama terendah n . Dengan cara ini, k c , dan f adalah koprime ke bilangan prima k - 1 pertama.nycn=halkfhalknkcfk-1

Ini mengarah ke rumus rekursif . Secara khusus, jumlah kosong jikac=0, jadiϕ(y,0)=y.ϕ(y,c)=y-1kcϕ(yhalk,k-1)c=0ϕ(y,0)=y

Kami sekarang memiliki formula yang memungkinkan kami untuk menghitung hanya dengan menghasilkan π ( 3 √ pertamaπ(x)bilangan prima (jutaan vs miliaran).π(x23)

Algoritma

Kita harus menghitung , di manapbisa serendah3π(xhal)p . Meskipun ada cara lain untuk melakukan ini (seperti menerapkan rumus kami secara rekursif), cara tercepat tampaknya adalah untuk menghitung semua bilangan prima hingga3x3 , yang dapat dilakukan dengan ayakan Eratosthenes.x23

Pertama, kami mengidentifikasi dan menyimpan semua bilangan prima di , dan menghitungπ( 3 [1,x]danπ(π(x3)pada saat bersamaan. Kemudian, kita menghitung xπ(x) untuk semuakdalam(π(3xpkk, dan hitung bilangan prima hingga masing-masing hasil bagi.(π(x3),π(x)]

Juga, memiliki bentuk tertutupπ( 3 π(x3)<kπ(x)(π(pk)+1) , yang memungkinkan kita untuk menyelesaikan perhitunganP2(x,π(3π(x3)π(x))(π(x3)+π(x)12P2(x,π(x3)) .

Itu meninggalkan perhitungan , yang merupakan bagian paling mahal dari algoritma. Cukup menggunakan rumus rekursif akan membutuhkan panggilan fungsi 2 c untuk menghitung ϕ ( y , c )ϕ2cϕ(y,c) .

Pertama-tama, untuk semua nilai c , jadi ϕ ( y , c ) = y - 1ϕ(0,c)=0c . Dengan sendirinya, pengamatan ini sudah cukup untuk membuat perhitungan layak. Ini karena angka di bawah 2 10 9ϕ(y,c)=y1kc,pkyϕ(ypk,k1)2109 lebih kecil dari produk dari sepuluh bilangan prima yang berbeda, sehingga sebagian besar dari jumlah puncak menghilang.

Juga, dengan mengelompokkan dan yang pertama c ' summands dari definisi φ , kita mendapatkan rumus alternatif φ ( y , c ) = φ ( y , c ' ) - Σ c ' < k c , p ky φ ( yycϕ. Dengan demikian, precomputingφ(y,c')untuk tetapc'dan nilai-nilai yang sesuai dariymenyimpan sebagian besar fungsi panggilan tersisa dan perhitungan terkait.ϕ(y,c)=ϕ(y,c)-c<kc,halkyϕ(yhalk,k-1)ϕ(y,c)cy

Jika , maka ϕ ( m c , c ) = φ ( m c ) , karena bilangan bulat dalam [ 1 , m c ] yang dapat dibagi dengan tidak ada p 1 , , p cmc=1kchalkϕ(mc,c)=φ(mc)[1,mc]hal1,,halc tepatnya mereka yang koprime ke . Juga, karena gcd ( z + m c , mmc , kita memiliki ϕ ( y , c ) = ϕ ( ygcd(z+mc,mc)=gcd(z,mc).ϕ(y,c)=ϕ(ymcmc,c)+ϕ(y

Karena fungsi total Euler adalah multiplikatif, , dan kami memiliki cara mudah untuk memperoleh ϕ ( y , c ) untuk semua y dengan mengkomputasi nilai hanya untuk y di [ 0 , m c )φ(mc)=1kcφ(pk)=1kc(pk1)ϕ(y,c)yy[0,mc) .

Juga, jika kita menetapkan , kita memperoleh ϕ ( y , c ) = ϕc=c1ϕ(y,c)=ϕ(y,c1)ϕ(ypc,c1) , definisi asli dari makalah Lehmer. Ini memberi kita cara sederhana untuk melakukan precompute untuk meningkatkan nilai cϕ(y,c)c .

Selain untuk precomputing untuk nilai c tertentu yang rendah, kami juga akan melakukan precompute untuk nilai y yang rendahϕ(y,c)cy , memotong rekursi sesaat setelah jatuh di bawah ambang batas tertentu.

Penerapan

Bagian sebelumnya mencakup sebagian besar kode. Satu detail penting yang tersisa adalah bagaimana pembagian fungsiPhi dilakukan.

Karena komputasi hanya membutuhkan pembagian dengan π pertama ( 3 ϕbilangan prima, kita bisa menggunakanfungsi sebagai gantinya. Daripada hanya membagiydenganpprima, kita mengalikanyπ(x3)fastdivypy dengan sebagai gantinya dan pulihkanydp264pyp as . Karena cara multiplikasi integer diimplementasikan padax64, bagi dengan2dpy264 tidak diperlukan; lebih tinggi 64 bit d p ydisimpan dalam register mereka sendiri.264dpy

Perhatikan bahwa metode ini membutuhkan precomputing , yang tidak lebih cepat dari komputasi ydpyhal secara langsung. Namun, karena kita harus membaginya dengan bilangan prima yang sama berulang-ulang dan pembagian jauh lebih lambat daripada perkalian, ini menghasilkan percepatan yang penting. Rincian lebih lanjut tentang algoritma ini, serta bukti formal, dapat ditemukan di Division oleh Invariant Integers menggunakan Multiplication .


22
Seseorang tidak hanya melebihi Dennis?
Addison Crump

8
Jujur saya tidak percaya seberapa cepat ini. Saya belum punya waktu untuk melewati dan memahami apa yang terjadi tetapi saya benar-benar perlu.
Liam

27
@Liam saya sepenuhnya bermaksud menjelaskan bagaimana ini bekerja, tapi saya masih mencoba mempercepatnya. Saat ini, saya benar-benar berharap PPCG memiliki LaTeX ...
Dennis

15
Catatan menyenangkan: (Di komputer saya) Ini saat ini mengalahkan baik Mathematica builtin dan kimwalisch di pustaka primecount C ++ github, namun, saat ini satu-satunya entri yang melakukannya.
Michael Klein

10
@TheNumberOne shh jangan katakan padanya tentang hal itu ... orang lain mungkin perlu untuk mengalahkannya
Liam

24

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

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

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

#define PPROD  3*5*7

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    free(buf);
    free(primes);

    return mod_limit - sum + 3;
    }


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

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

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

Implementasi saringan erastotel berbasis bitmap. Ia melakukan langkah-langkah berikut:

  1. Pertama, buat pola bit berulang untuk mengisi ayakan, yang mencakup kelipatan 2,3,5,7
  2. Selanjutnya, gunakan metode saringan untuk menghasilkan array dari semua bilangan prima lebih kecil dari sqrt (n)
  3. Selanjutnya, gunakan daftar utama dari langkah sebelumnya untuk menulis ke dalam saringan. Ini dilakukan pada potongan saringan yang berukuran sekitar L1-cache, sehingga pemrosesan saringan tidak terus-menerus meremukkan cache L1; ini tampaknya membawa tentang percepatan 5x lebih dari tidak chunking.
  4. Akhirnya, lakukan penghitungan bit.

Dikompilasi dengan gcc primecount.c -O3 -lm -Walldan dijalankan di ubuntu 15.10 (64-bit) pada i7-4970k, dibutuhkan sekitar 2,2 detik untuk set penuh kasus skor. Waktu berjalan didominasi oleh langkah 3; ini bisa multithreaded jika diinginkan, karena potongannya independen; ini akan membutuhkan perhatian untuk memastikan bahwa batas-batas chunk selaras.

Ini mengalokasikan sedikit lebih banyak memori daripada yang dibutuhkan untuk ayakan; ini membuat ruang untuk overrun buffer akhir, yang diperlukan agar loop terbuka di langkah 3 agar berfungsi dengan baik.

Waktu resmi

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
Selamat datang di Programming Puzzles & Code Golf, dan selamat atas kiriman pertama bintang !
Dennis

Pertimbangkan untuk menggunakan -O3 -march=native. CPU Anda mendukung popcntinstruksi , dan kompiler terkadang dapat mengenali beberapa implementasi C murni dan mengompilasi dengan instruksi tunggal. (Atau lebih baik, gunakan __builtin_popcountllpada GNU C, seperti jawaban Dennis).
Peter Cordes

-march=nativepada Haswell CPU Anda juga akan memungkinkan BMI2 untuk instruksi pergeseran jumlah variabel yang lebih efisien. ( SHLX bukan warisan SHL yang perlu diperhitungkan cl.) CPU AMD Piledriver OP tidak memiliki BMI2, tetapi memang memiliki popcnt. Tapi CPU AMD menjalankan penghitungan variabel SHL lebih cepat dari CPU Intel, jadi kompilasi dengan BMI2 saat tuning mungkin masih tepat. Piledriver sangat berbeda dari Haswell sejauh optimasi mikro, tetapi meminta -march=nativeitu baik
Peter Cordes

12

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

Ini menggunakan metode Meissel-Lehmer.

Pengaturan waktu

$ 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

Waktu resmi

Karena ada jawaban lain dengan waktu yang sama, saya memilih untuk mendapatkan hasil yang lebih tepat. Saya menghitung waktu ini 100 kali. Skor tersebut adalah waktu berikut dibagi 100.

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

5
Juga, hanya sebagai catatan: kode ini 15.102,4 kali lebih cepat dari milik saya. +1
Addison Crump

12

Java, 25.725.315 detik pada mesin ini

Ini tidak akan menang , saya hanya ingin memposting jawaban yang tidak menggunakan saringan.

UPDATE: Saat ini peringkatnya sekitar 150.440.4386 kali lebih lambat dari skor terkemuka. Pilih mereka, jawaban mereka luar biasa.

Kode byte:

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

Kode sumber:

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

Ternyata pengoptimal itu, pada kenyataannya, meningkatkan waktu yang dibutuhkan. >.> Sialan.

Input di bawah 1000 tampaknya memakan waktu rata-rata .157 di komputer saya (kemungkinan karena pemuatan kelas ಠ_ಠ), tetapi setelah sekitar 1e7, ia jadi rewel.

Daftar waktu:

> 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 saat ini berjalan pada CPU 100% yang konsisten saat ini. Ini benar-benar efisien, apa yang Anda bicarakan?
Addison Crump

dapatkah Anda memberi saya tutorial berhenti tentang cara java (karena C / C ++> java). Saya kompilasi dengan javac voteToClose.java(saya berganti nama menjadi kelas) dan kemudian apa?
Liam

@Liamjava voteToClose <input>
Addison Crump

1
Tunggu ... Mengapa kode byte mengatakan cafe babe?
Cyoce

12
@Cyoce Semua file kelas Java dipimpin dengan 0xCAFEBABE.
Addison Crump

8

Rust, 0,37001 dtk (12 Juni 2016)

Sekitar 10 kali lebih lambat dari pada Cjawaban Dennis , tetapi 10 kali lebih cepat dari entri Python-nya. Jawaban ini dimungkinkan oleh @Shepmaster dan @Veedrac yang membantu memperbaikinya pada Tinjauan Kode . Ini diambil kata demi kata dari posting @ 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
}

Jangka waktu dengan: di time ./time.shmana time.shterlihat seperti:

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

Ini outputnya.

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

Akhirnya sempat memperbaiki ini, dan itu lebih kecil / sederhana dan JAUH lebih cepat dari sebelumnya. Alih-alih metode brute force yang lebih lambat, ia menggunakan Saringan Eratosthenes bersama dengan struktur data yang lebih efisien, sehingga sekarang dapat benar-benar selesai dalam waktu yang terhormat (sejauh yang dapat saya temukan di internet, ini adalah JS prime count tercepat) berfungsi di luar sana).

Beberapa waktu demo (i7-3770k):

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

Kenapa +=1dan tidak ++?
ETHproduksi

@ ETHproductions Tergantung pada apakah yang Anda maksud pra atau pasca kenaikan i++harus menahan perubahan nilai untuk op lain, yang pada skala ini menyebabkan hit kinerja kecil namun nyata. Saya tidak menguji pra-kenaikan, tapi saya kira itu akan sama dengan +=1.
Mwr247

Tetapi +=1perlu dialokasikan 1ke dalam memori. Kupikir. Jika aku jadi kamu, aku akan menggunakan ++i. Saya pikir ada satu instruksi untuk meningkatkan nilai, jadi, saya tidak yakin.
Ismael Miguel

Mengapa begitu kental? Ini bukan kode-golf , dan ini sangat sulit dibaca.
Cyoce

Juga, mungkin membantu mengubah (...)|0;i=0ke(...)|(i=0)
Cyoce

6

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

Kompilasi dengan g++ -O2 -m64 -march=native -ftree-vectorize -std=c++11 numprimes.cpp. Opsi-opsi ini penting. Anda juga harus memasang Boost . Di Ubuntu ini tersedia dengan menginstal libboost-all-dev.

Jika Anda menggunakan Windows, saya dapat merekomendasikan untuk menginstal g++ dan Meningkatkan melalui MSYS2 . Saya telah menulis tutorial yang bagus tentang cara menginstal MSYS2. Setelah mengikuti tutorial Anda dapat menginstal Boost menggunakan 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;
}

Di mesin saya ini berjalan dalam 4,8 detik untuk 1907000000 (1.9e9).

Kode di atas adalah repurposed dari pustaka C ++ pribadi saya , jadi saya sudah mulai.

Waktu resmi

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. Itu cepat. Apa mesin anda?
Addison Crump

@VoteToClose Intel i5-4670k menjalankan 64-bit Windows 7.
orlp

mau menambahkan penjelasan?
Liam

@Liam Ini hanya saringan yang memiliki nomor yang merupakan kelipatan dari 2 dan 3 yang tersisa dari saringan.
orlp

3

C ++, 2.47215s (29 Feb 2016)

Ini adalah versi (ceroboh) multi-threaded jawaban saya yang lain.

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

Menggunakan saringan Eratosthenes yang tersegmentasi dengan faktorisasi roda 6 untuk melewati semua kelipatan 2/3. Memanfaatkan POSIXffsll untuk melewati nilai komposit berurutan.

Untuk mengkompilasi:

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

pengaturan waktu tidak resmi

Tercatat dengan Intel i5-6600k di Ubuntu 15.10, kasing 1907000000 diambil 0.817s.

Waktu resmi

Untuk mendapatkan waktu yang lebih tepat, saya menghitung 100 kali, kemudian membagi waktu dengan 100.

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

Karena ini dan jawaban python @Dennis sangat dekat, saya dapat mengembalikannya untuk hasil yang lebih akurat.
Liam

Wow wow wow. Bagi saya ini lebih tidak masuk akal dibandingkan CJam atau Pyth. Saya akan menyebutnya monster bit-shift! +1
Tamoghna Chowdhury

Sebagai tambahan, dapatkah Anda mencoba CUDA / OpenCL untuk speedup GPU? Jika saya tahu lebih banyak C, saya mungkin tahu.
Tamoghna Chowdhury

Ya, saya kira saya sedikit berlebihan dengan bitshifting / masking: PI tidak tahu apakah GPGPU akan membantu di sini atau tidak; satu-satunya area yang saya bisa lihat mungkin membantu adalah dalam pra-pengayakan bilangan prima kecil, dan bahkan kemudian kecepatan transfer data mungkin cukup untuk membunuh itu. Yang masih membuat saya gelisah adalah bahwa saya masih libur dengan faktor 10 atau lebih dari implementasi saringan tercepat yang pernah saya lihat
helloworld922

2

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

Simpan sebagai pi.c, kompilasi sebagai gcc -o pi pi.c, jalankan sebagai ./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;
}

Perlu banyak memori untuk dijalankan! Jika perangkat keras Anda tidak dapat menyimpan hingga dua gigabyte memori nyata, maka program akan crash atau berjalan sangat lambat karena meronta-ronta VMM dan HD.

Perkiraan waktu pada perangkat keras saya adalah 1,239 × 10 -8 · n 1,065 s. Misalnya, input n = 2 × 10 9 membutuhkan sekitar 100 detik untuk dijalankan.

Waktu resmi

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

Ini bekerja menggunakan ayakan eratosthenes? Saya akan mengatur waktu ketika saya tiba di rumah
Liam

Saya melakukan segmentasi pada kasus pertama (yang lain berjalan dengan baik). Itu terjadi setelah ~ 1 menit runtime. Saya menambahkan if (p==NULL) {exit(1);}baris ke kode, jadi saya tidak percaya malloc gagal (juga akan gagal pada awalnya, bukan 1 menit). Gagasan tentang apa yang terjadi?
Liam

Banyak sistem, termasuk Linux, melakukan alokasi optimis. Misalnya jika Anda meminta 1 Gb, ia akan "memberikan" itu kepada Anda, tetapi ketika Anda benar-benar pergi untuk menggunakannya, dan jika sistem tidak dapat menemukannya, itu akan macet. Jika ini masalahnya, kemungkinan akan crash pada memset. Menit yang diperlukan adalah waktu yang dihabiskan untuk mencoba menyatukan tumpukan menjadi blok yang berdekatan. Periksa juga sistem Anda adalah sizeof (bool) == 1. Jika itu == 4, maka saya dapat menulis ulang ini untuk menggunakan char.

Saya sudah memeriksa. Bool adalah 1 byte. Apakah mungkin untuk hanya meminta bentuk 2 * 10 ^ 9 byte memori di stack? Yaitu mendeklarasikan variabel global yang (pada gcc) saya percaya akan diinisiasi ke 0. Ini akan membutuhkan penggunaan charsebagai gantinya menurut saya.
Liam

1
@Liam Sulit dikatakan. Signed integer overflow adalah perilaku yang tidak terdefinisi, jadi tanpa melihat rakitan yang dihasilkan, sulit untuk memprediksi apa yang dilakukan kompiler.
Dennis

2

Julia, 1 jt 21.1329d

Saya ingin membuat sesuatu yang sedikit lebih cepat, tetapi untuk sekarang ini adalah implementasi yang agak naif dari Saringan 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))

Dapatkan versi terbaru Julia untuk sistem Anda di sini . Pastikan executable Julia ada di jalur Anda. Simpan kode sebagai sieve.jldan jalankan dari baris perintah seperti julia sieve.jl N, di mana Ninput.

Waktu resmi

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
Saya menerapkan Saringan Atkin dan implementasi saya untuk itu lebih lambat. >: U
Alex A.

@Liam Whoa. Saya bertanya-tanya mengapa waktu resmi lebih panjang daripada yang tidak resmi saya. Waktu resmi cukup mengerikan.
Alex A.

Nah waktu resmi adalah untuk semua skor kasus bersama. Yang tidak resmi pergi nomor dengan nomor. Juga, komputer saya mungkin tidak secepat komputer Anda.
Liam

@Liam Oh, itu lebih masuk akal. Dang, saya pikir ini layak. Oh well, kembali ke papan gambar.
Alex A.

Saya akan mencuri algoritma Dennis ... supaya saya bisa mengerti seberapa cepat itu.
Liam

2

Java, 42.663122s * (3 Mar 2016)

* ini dihitung waktunya secara internal oleh program (pada komputer OP)

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

Mengikuti tradisi PPCG yang hebat dalam mendokumentasikan diri sendiri (meskipun tidak dalam arti literal: p).

Ini untuk membuktikan bahwa Java cukup cepat untuk dapat bersaing dengan bahasa VM lain saat menggunakan algoritma yang sama.

Jalankan informasi

Jalankan sebagai Anda akan memiliki jawaban @ CoolestVeto, tetapi saya tidak perlu argumen baris perintah, itu bisa mendapatkannya dari STDIN.

Tweak NUM_THREADSkonstanta untuk mengaturnya menjadi 2x jumlah inti asli Anda untuk kinerja maksimum (seperti yang saya amati - Dalam kasus saya, saya memiliki 8 core virtual sehingga diatur ke 16, OP mungkin menginginkan 12 untuk prosesor hexa-core-nya).

Ketika saya menjalankan tes ini saya menggunakan JDK 1.7.0.45 dengan BlueJ 3.1.6 (IntelliJ sedang memperbarui) pada Windows 10 Enterpise x64 pada laptop ASUS K55VM (Core i7 3610QM, 8GB RAM). Google Chrome 49.0 64-bit dengan 1 tab (PPCG) terbuka dan QBittorrent yang mengunduh 1 file sedang berjalan di latar belakang, 60% penggunaan RAM pada saat mulai dijalankan.

Pada dasarnya,

javac PrimeCounter.java
java PrimeCounter

Program ini akan memandu Anda melalui sisanya.

Pengaturan waktu dilakukan oleh Java bawaan System.nanoTime().

Detail algoritma:

Memiliki 3 varian untuk kasus penggunaan yang berbeda - versi naif seperti @ CoolestVeto (tapi multithreaded) untuk input di bawah 2 ^ 15, dan saringan Eratosthenes bitmasked dengan eliminasi aneh untuk input di atas 2 ^ 28, dan saringan normal Eratosthenes dengan 2/3/5/7 faktorisasi roda untuk pra-eliminasi kelipatan.

Saya menggunakan saringan bitmasked untuk menghindari argumen JVM khusus untuk kasus uji terbesar. Jika itu bisa dilakukan, overhead untuk perhitungan jumlah dalam versi bitmasked dapat dihilangkan.

Inilah hasilnya:

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

Mengeluarkan hanya hasil pi (n) (tanpa petunjuk) dapat menghemat waktu, karena STDOUT adalah ... yah, katakanlah itu bisa menjadi sedikit lebih cepat.
user48538

@ zyabin101, jika ada yang memiliki kesabaran untuk membaca kode, dia akan mengerti bahwa STDOUT latency telah diperhitungkan.
Tamoghna Chowdhury

Juga untuk pengaturan waktu, saya telah mengirim stdout ke / dev / null
Liam

@Liam saya kira Anda harus membuat pengecualian dalam kasus saya, kalau begitu. Anda bisa men-tweak metode utama untuk argumen baris perintah, tetapi programnya tetap mengatur waktu. Lihat saja. Silahkan?
Tamoghna Chowdhury

Tentu saja saya akan. Saya akan melakukannya besok. Jika saya memiliki masalah saya akan menelepon Anda di chat
Liam

2

Python 3

import sys

sys.setrecursionlimit(sys.maxsize)

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

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

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

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

print(sum(p) - 2)

Menggunakan Saringan Eratosthenes. Berjalan rata-rata di 8.775smana n = 10^7. Untuk waktu, saya menggunakan timeperintah builtin . Sebagai contoh:

$ time python3 test.py 90
24

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

Ini saringan! Saya tidak bisa menggunakan ini di Jawa karena tidak suka berapa banyak memori yang digunakan array boolean. D:
Addison Crump

kesalahan memori pada kasus yang lebih besar.
Liam

Kasus yang mana? Saya yakin saya sudah memperbaikinya. @Liam
Zach Gates

2
@VoteToClose Maka jangan gunakan array Boolean. Gunakan array integer dan bit shifting / masking, dengan masing-masing bit mewakili nilai Boolean.
mbomb007

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

1

C ++, 9.3221s (29 Feb 2016)

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

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

typedef std::vector<uint64_t> buf_type;

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

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

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

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

Menggunakan saringan Eratosthenes yang tersegmentasi dengan faktorisasi roda 6 untuk melewati semua kelipatan 2/3. Manfaatkan POSIXffsll untuk melewati nilai komposit berurutan.

Dapat berpotensi dipercepat dengan membuat saringan tersegmentasi bekerja secara paralel.

Untuk mengkompilasi:

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

pengaturan waktu tidak resmi

Tercatat dengan Intel i5-6600k di Ubuntu 15.10, kasing 1907000000 diambil 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

Waktu Resmi

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.