Tính số nguyên tố lên tới n


64

π ( n ) là số nguyên tố nhỏ hơn hoặc bằng n .

Đầu vào: một số tự nhiên, n .

Đầu ra: π (n).

Ghi điểm: Đây là một thử thách . Điểm sẽ là tổng số lần cho các trường hợp điểm. Tôi sẽ thời gian mỗi mục trên máy tính của tôi.

Quy tắc và chi tiết

  • Code của bạn nên làm việc cho n lên đến 2 tỷ đồng (2000000000).

  • Xây dựng mà tầm thường hóa điều này là không được phép. Điều này bao gồm các hàm π được xây dựng hoặc danh sách các giá trị cho π ( n ).

  • Được xây dựng mà kiểm tra tính nguyên thủy hoặc tạo số nguyên tố không được phép. Điều này bao gồm các danh sách các số nguyên tố, có thể không được tra cứu bên ngoài hoặc mã hóa cục bộ, ngoại trừ liên quan đến điểm đạn tiếp theo.

  • Bạn có thể mã hóa số nguyên tố lên đến và bao gồm 19 và không cao hơn.

  • việc thực hiện của bạn nên được xác định. Điều này có nghĩa là với một n cụ thể , mã của bạn sẽ chạy trong (khoảng) cùng một lượng thời gian.

  • Các ngôn ngữ được sử dụng phải có sẵn miễn phí trên Linux (Centos 7). Hướng dẫn nên được bao gồm về cách chạy mã của bạn. Bao gồm chi tiết trình biên dịch / thông dịch nếu cần thiết.

  • Thời gian chính thức sẽ từ máy tính của tôi.

  • Khi đăng bài, vui lòng bao gồm thời gian tự đo trên một số / tất cả các trường hợp kiểm tra / điểm, chỉ để cho tôi ước tính tốc độ mã của bạn đang chạy.

  • Đệ trình phải phù hợp với một bài trả lời cho câu hỏi này.

  • Tôi đang chạy 64 bit centos7. Tôi chỉ có 8GB RAM và 1GB trao đổi. Mô hình cpu là: Bộ xử lý sáu nhân AMD FX (tm) -6300.

Các trường hợp thử nghiệm ( nguồn ):

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

Điểm trường hợp ( cùng nguồn )

Như thường lệ, những trường hợp này có thể thay đổi. Tối ưu hóa cho các trường hợp cho điểm là không được phép. Tôi cũng có thể thay đổi số lượng các trường hợp trong một nỗ lực để cân bằng thời gian chạy hợp lý và kết quả chính xác.

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

Thời lượng

Vì đây là một thử thách và các mục nhập sẽ được chạy trên máy tính của tôi, tôi bảo lưu quyền dừng các mục nhập thời gian sau 2 tuần. Sau thời điểm này, các mục vẫn được chấp nhận, nhưng không có gì đảm bảo rằng chúng được chính thức tính thời gian.

Đã nói điều này, tôi không mong đợi quá nhiều câu trả lời cho thử thách này và tôi có thể sẽ tiếp tục thời gian trả lời mới vô thời hạn.

Chấm điểm

Tôi đã tính thời gian cho các mục nhanh hơn với tập lệnh sau:

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

timeghi vào stderr, vì vậy tôi đã gửi stderrđến một tệp nhật ký bằng cách sử dụng exec 2 >> <filename>. Bạn có thể nhận thấy rằng stdoutđược gửi đến /dev/null. Đây không phải là một vấn đề, bởi vì tôi đã xác minh rằng các chương trình đang tạo ra đầu ra chính xác.

Tôi đã chạy đoạn timeall.shscript trên 10 lần bằngfor i in {1..10}; do ./timeall.sh; done;

Sau đó tôi tính trung bình real timeđiểm cho mỗi mục.

Lưu ý rằng không có chương trình nào khác đang chạy trên máy tính của tôi trong khi định thời gian.

Ngoài ra, thời gian chính thức đã được thêm vào mỗi mục. Vui lòng kiểm tra lại trung bình của riêng bạn.


Điều gì ngăn chúng ta sử dụng bảng tra cứu với các giá trị 2e9 đầu tiên của pi (n)? Điều đó có được chấp nhận không? (Tuy nhiên, không chắc là nó sẽ nhanh như thế nào, bởi vì nó sẽ là một cái bàn lớn)
Luis Mendo

@DonMuesli Điều đó sẽ không được chấp nhận (đi ngược lại tinh thần của thử thách), tôi đã chỉnh sửa để khiến nó bị cấm hoàn toàn ngay bây giờ.
Liam

8
Thật nguy hiểm khi đề cập đến "tinh thần" của thử thách. "Chống lại tinh thần" của bạn có thể là "mánh khóe tuyệt vời" của người khác :-) Tốt hơn là bạn đã nói rõ
Luis Mendo

1
Tích hợp gì? Tôi có một chức năng liệt kê danh sách trong một thư viện. Tôi có thể sử dụng nó? Nếu không, tôi có thể sao chép mã nguồn của thư viện trong chương trình của mình và sử dụng mã đó không?
nimi

1
@Liam: Vâng, tôi biết, nhưng những gì được tính là tích hợp? Là sao chép mã nguồn từ thư viện có tích hợp không?
nimi

Câu trả lời:


119

C, 0,026119 (ngày 12 tháng 3 năm 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);
}

Điều này sử dụng phương pháp Meissel-Lehmer .

Thời gian

Trên máy của tôi, tôi nhận được khoảng 5,7 mili giây cho các trường hợp thử nghiệm kết hợp. Đây là trên Intel Core i7-3770 với RAM DDR3 ở mức 1867 MHz, chạy 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

phương sai tăng quá cao , tôi đang sử dụng thời gian từ trong chương trình cho thời gian chạy không chính thức. Đây là tập lệnh tính trung bình của thời gian chạy kết hợp.

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

Thời gian chính thức

Lần này là để làm các trường hợp điểm số 1000 lần.

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

Làm thế nào nó hoạt động

Công thức

Đặt là số nguyên dương.x

Mỗi số nguyên dương thỏa mãn chính xác một trong các điều kiện sau.nx

  1. n=1

  2. n chia hết cho số nguyên tố trong .p[1,x3]

  3. n=pq , trong đó và là các số nguyên tố (không nhất thiết phải phân biệt) trong .pq(x3,x23)

  4. n là số nguyên tố vàn>x3

Gọi là số nguyên tố sao cho . Có các số thuộc loại thứ tư.π(y)ppyπ(x)π(x3)

Gọi biểu thị số lượng số nguyên dương là tích của số nguyên tố chính xác không nằm trong số các số nguyên tố đầu tiên . Có các số thuộc loại thứ ba.Pk(y,c)mykcP2(x,π(x3))

Cuối cùng, hãy để biểu thị số lượng số nguyên dương các số nguyên tố đầu tiên . Có các số thuộc loại thứ hai.ϕ(y,c)kycxϕ(x,π(x3))

Vì có số trong tất cả các loại,x

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

và do đó,

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

Các số trong danh mục thứ ba có một đại diện duy nhất nếu chúng tôi yêu cầu và, do đó . Theo cách này, sản phẩm của các số nguyên tố và nằm trong danh mục thứ ba khi và chỉ khi , do đó, có giá trị có thể có cho với giá trị cố định là và , trong đó biểu thị số nguyên tố .pqpxpqx3<pqxpπ(xp)π(p)+1qpP2(x,π(x3))=π(x3)<kπ(x)(π(xpk)π(pk)+1)pkkth

Cuối cùng, tất cả các số nguyên dương được không nguyên tố cùng nhau để là người đầu tiên số nguyên tố có thể được biểu diễn trong thời trang độc đáo như , nơi là yếu tố quan trọng nhất của . Theo cách này, và là số nguyên tố của các số nguyên tố đầu tiên .nycn=pkfpknkcfk1

Điều này dẫn đến công thức đệ quy . Cụ thể, tổng là trống nếu , vì vậy .ϕ(y,c)=y1kcϕ(ypk,k1)c=0ϕ(y,0)=y

Bây giờ chúng ta có một công thức cho phép chúng ta tính toán bằng cách chỉ tạo các số nguyên tố đầu tiên (hàng triệu so với hàng tỷ).π(x)π(x23)

Thuật toán

Chúng ta sẽ cần tính toán , trong đó có thể đạt mức thấp như . Mặc dù có nhiều cách khác để làm điều này (như áp dụng công thức của chúng tôi một cách đệ quy), cách nhanh nhất dường như là liệt kê tất cả các số nguyên tố lên đến , có thể được thực hiện bằng sàng Eratosthenes.π(xp)px3x23

Đầu tiên, chúng tôi xác định và lưu trữ tất cả các số nguyên tố trong và tính toán và cùng một lúc. Sau đó, chúng tôi tính toán cho tất cả trong và đếm các số nguyên tố theo từng thương số kế tiếp .[1,x]π(x3)π(x)xpkk(π(x3),π(x)]

Ngoài ra, có dạng đóng , trong đó cho phép chúng tôi hoàn thành việc tính toán .π(x3)<kπ(x)(π(pk)+1)π(x3)π(x))(π(x3)+π(x)12P2(x,π(x3))

Điều đó làm cho tính toán của , phần đắt nhất của thuật toán. Chỉ cần sử dụng công thức đệ quy sẽ yêu cầu các hàm gọi để tính toán .ϕ2cϕ(y,c)

Trước hết, cho tất cả các giá trị của , vì vậy . Chính nó, quan sát này đã đủ để làm cho tính toán khả thi. Điều này là do bất kỳ số nào dưới nhỏ hơn sản phẩm của mười số nguyên tố khác biệt, do đó, phần lớn các triệu hồi áp đảo biến mất.ϕ(0,c)=0cϕ(y,c)=y1kc,pkyϕ(ypk,k1)2109

Ngoài ra, bằng cách nhóm và các triệu tập đầu tiên của định nghĩa , chúng ta có được công thức thay thế . Do đó, tiền mã hóa cho một cố định và các giá trị thích hợp của lưu hầu hết các lệnh gọi hàm còn lại và các tính toán liên quan.ycϕϕ(y,c)=ϕ(y,c)c<kc,pkyϕ(ypk,k1)ϕ(y,c)cy

Nếu , thì , vì các số nguyên trong không chia hết cho chính xác là những phần tử tương ứng với . Ngoài ra, vì , chúng tôi có .mc=1kcpkϕ(mc,c)=φ(mc)[1,mc]p1,,pcmcgcd(z+mc,mc)=gcd(z,mc)ϕ(y,c)=ϕ(ymcmc,c)+ϕ(y

Vì hàm tổng của Euler là số nhân, và chúng tôi có một cách dễ dàng để lấy được cho tất cả bằng cách tính trước các giá trị cho chỉ những trong .φ(mc)=1kcφ(pk)=1kc(pk1)ϕ(y,c)yy[0,mc)

Ngoài ra, nếu chúng ta đặt , chúng ta sẽ có được , định nghĩa ban đầu từ bài báo của Lehmer. Điều này cho chúng ta một cách đơn giản để tiền mã hóa để tăng giá trị của .c=c1ϕ(y,c)=ϕ(y,c1)ϕ(ypc,c1)ϕ(y,c)c

Ngoài việc tính toán trước cho một giá trị thấp nhất định của , chúng tôi cũng sẽ tính toán trước cho các giá trị thấp của , cắt ngắn đệ quy sau khi giảm xuống dưới một ngưỡng nhất định.ϕ(y,c)cy

Thực hiện

Phần trước bao gồm hầu hết các phần của mã. Một chi tiết quan trọng còn lại là cách phân chia chức năng Phiđược thực hiện.

Vì tính toán chỉ yêu cầu chia cho các số nguyên tố đầu tiên, nên chúng ta có thể sử dụng hàm thay thế. Thay vì chỉ đơn giản là chia một bởi một số nguyên tố , chúng ta nhân bởi thay vào đó và phục hồi như . Do cách nhân số nguyên được triển khai trên x64 , nên không cần chia ; 64 bit cao hơn được lưu trữ trong thanh ghi riêng của chúng.ϕπ(x3)fastdivypydp264pyp 264dpydpy264264dpy

Lưu ý rằng phương pháp này yêu cầu tính toán trước , không nhanh hơn so với tính toán trực tiếp . Tuy nhiên, vì chúng ta phải chia cho cùng một số nguyên tố lặp đi lặp lại và phép chia chậm hơn nhiều so với phép nhân, điều này dẫn đến việc tăng tốc quan trọng. Thông tin chi tiết về thuật toán này, cũng như bằng chứng chính thức, có thể được tìm thấy trong Division by Invariant Integers bằng phép nhân .ydpyp


22
Một người không chỉ đơn giản là vượt qua Dennis?
Addison Crump

8
Thành thật mà nói tôi không thể tin được nó nhanh như thế nào. Tôi đã không có thời gian để đi qua và hiểu những gì đang xảy ra nhưng tôi thực sự cần phải làm.
Liam

27
@Liam Tôi hoàn toàn có ý định giải thích cách thức hoạt động của nó, nhưng tôi vẫn đang cố gắng tăng tốc nó. Ngay bây giờ, tôi thực sự mong muốn PPCG có LaTeX ...
Dennis

15
Lưu ý thú vị: (Trên máy của tôi) Điều này hiện đang đánh bại cả phần mềm dựng sẵn và kimwalisch của thư viện Cithub trên thư viện C ++ của github, tuy nhiên, hiện tại đây là mục duy nhất làm như vậy.
Michael Klein

10
@TheNumberOne đừng nói với anh ấy về điều đó ... những người khác có thể cần điều đó để đánh bại anh ấy
Liam

24

C99 / C ++, 8,9208 (28 tháng 2 năm 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) );
    }

Việc thực hiện sàng dựa trên bitmap dựa trên bitmap. Nó thực hiện các bước sau:

  1. Đầu tiên, tạo một mẫu bit lặp lại để lấp đầy sàng, bao gồm bội số của 2,3,5,7
  2. Tiếp theo, sử dụng phương pháp sàng để tạo ra một mảng của tất cả các số nguyên tố nhỏ hơn sqrt (n)
  3. Tiếp theo, sử dụng danh sách nguyên tố từ bước trước để ghi vào sàng. Điều này được thực hiện trên các khối của sàng có kích thước xấp xỉ L1-cache, do đó quá trình xử lý sàng không liên tục đập bộ đệm L1; điều này dường như mang lại tốc độ tăng gấp 5 lần so với việc không chunk.
  4. Cuối cùng, thực hiện đếm bit.

Được biên dịch gcc primecount.c -O3 -lm -Wallvà chạy trên Ubuntu 15.10 (64-bit) trên i7-4970k, mất khoảng 2,2 giây cho toàn bộ các trường hợp điểm. Thời gian chạy bị chi phối bởi bước 3; điều này có thể là đa luồng nếu muốn, vì các khối là độc lập; điều này sẽ đòi hỏi một số sự quan tâm để đảm bảo rằng ranh giới khối được căn chỉnh phù hợp.

Nó phân bổ bộ nhớ nhiều hơn một chút so với cần thiết cho sàng; điều này tạo chỗ cho một số lỗi tràn bộ đệm, cần thiết để vòng lặp không được kiểm soát ở bước 3 hoạt động bình thường.

Thời gian chính thức

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
Chào mừng bạn đến với Câu đố lập trình & Code Golf, và chúc mừng bài đăng đầu tiên xuất sắc !
Dennis

Cân nhắc sử dụng -O3 -march=native. CPU của bạn hỗ trợ popcnthướng dẫn và trình biên dịch đôi khi có thể nhận ra một số triển khai C thuần túy của nó và biên dịch thành một lệnh đơn. (Hoặc tốt hơn, sử dụng __builtin_popcountlltrên GNU C, như câu trả lời của Dennis).
Peter Cordes

-march=nativetrên CPU Haswell của bạn cũng sẽ kích hoạt BMI2 cho các hướng dẫn thay đổi số đếm hiệu quả hơn. ( SHLX thay vì SHL kế thừa cần tính cl.) CPU AMD Piledriver của OP không có BMI2, nhưng nó có popcnt. Nhưng CPU AMD chạy SHL đếm biến đổi nhanh hơn CPU Intel, do đó, biên dịch với BMI2 trong khi điều chỉnh vẫn có thể phù hợp. Piledriver khá khác biệt so với Haswell cho đến khi tối ưu hóa vi mô, nhưng yêu cầu -march=nativelà tốt
Peter Cordes

12

Python 2 (PyPy 4.0), 2.36961 (ngày 29 tháng 2 năm 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])

Điều này sử dụng phương pháp Meissel-Lehmer.

Thời gian

$ 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

Thời gian chính thức

Vì có một câu trả lời khác với thời gian tương tự, tôi đã chọn để có kết quả chính xác hơn. Tôi đã hẹn giờ này 100 lần. Điểm số là lần sau chia cho 100.

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

5
Ngoài ra, chỉ cần lưu ý: mã này nhanh hơn 15.102,4 lần so với của tôi. +1
Addison Crump

12

Java, 25.725.315 giây trên máy này

Điều này sẽ không chiến thắng , tôi chỉ muốn đăng một câu trả lời không sử dụng bất kỳ sàng nào.

CẬP NHẬT: Điều này hiện được xếp hạng chậm hơn khoảng 150.440.4386 lần so với điểm số hàng đầu. Đi lên - bỏ phiếu cho họ, câu trả lời của họ thật tuyệt vời.

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

Mã nguồn:

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

Thực tế, tối ưu hóa là tăng thời gian thực hiện. >.> Chết tiệt.

Đầu vào dưới 1000 dường như mất thời gian trung bình 0,17 giây trên máy tính của tôi (có thể do tải lớp ಠ_ಠ), nhưng qua khoảng 1e7 thì nó trở nên khó khăn.

Danh sách thời gian:

> 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 hiện đang chạy với CPU 100% nhất quán ngay bây giờ. Điều này là hoàn toàn hiệu quả, bạn đang nói về cái gì?
Addison Crump

bạn có thể cho tôi một hướng dẫn bỏ về cách java (vì C / C ++> java). Tôi biên dịch với javac voteToClose.java(tôi đổi tên lớp) và sau đó là gì?
Liam

@Liamjava voteToClose <input>
Addison Crump

1
Đợi ... Tại sao mã byte nói cafe babe?
Cyoce

12
@Cyoce Tất cả các tệp lớp Java đều có 0xCAFEBABE.
Addison Crump

8

Rust, 0,37001 giây (ngày 12 tháng 6 năm 2016)

Chậm hơn khoảng 10 lần so với chậm hơn Ccâu trả lời của Dennis , nhưng nhanh hơn 10 lần so với mục Python của anh ấy. Câu trả lời này được thực hiện bởi @Shepmaster và @Veedrac, người đã giúp cải thiện nó trên Đánh giá mã . Nó được lấy nguyên văn từ bài viết của @ 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
}

Hẹn giờ với: time ./time.shnơi time.shtrông như:

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

Đây là đầu ra.

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

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

8

Node.js (JavaScript / ES6), 83.549 (11 tháng 11 năm 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)

Cuối cùng cũng có xung quanh để làm lại điều này, và nó vừa nhỏ hơn / đơn giản hơn và NHIỀU nhanh hơn trước. Thay vì phương pháp vũ lực chậm hơn, nó sử dụng Sàng của Eratosthenes bên cạnh các cấu trúc dữ liệu hiệu quả hơn, để giờ đây nó có thể thực sự kết thúc trong một thời gian đáng nể (theo như tôi có thể tìm thấy trên internet, đó là số nguyên tố JS nhanh nhất chức năng ngoài kia).

Một số lần giới thiệu (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

Tại sao +=1và không ++?
Sản phẩm ETH

@ETHproductions Phụ thuộc vào nếu bạn có nghĩa là trước hoặc sau tăng. i++phải giữ thay đổi giá trị cho một op khác, ở quy mô này dẫn đến một hiệu suất nhỏ nhưng đáng chú ý. Tôi đã không kiểm tra mức tăng trước, nhưng tôi nghi ngờ nó sẽ giống như +=1.
Mwr247

Nhưng +=1cần phân bổ 1vào bộ nhớ. Tôi nghĩ. Nếu tôi là bạn, tôi sẽ sử dụng ++i. Tôi nghĩ rằng có một hướng dẫn duy nhất để tăng giá trị, vì vậy, tôi không chắc chắn.
Ismael Miguel

Tại sao nó lại cô đặc như vậy? Đây không phải là môn đánh gôn , và điều này thực sự khó đọc.
Cyoce

Ngoài ra, nó có thể giúp thay đổi (...)|0;i=0thành(...)|(i=0)
Cyoce

6

C ++ 11, 22.6503s (28 tháng 2 năm 2016)

Biên dịch với g++ -O2 -m64 -march=native -ftree-vectorize -std=c++11 numprimes.cpp. Các tùy chọn này rất quan trọng. Bạn cũng cần phải cài đặt Boost . Trên Ubuntu, điều này có sẵn bằng cách cài đặt libboost-all-dev.

Nếu bạn đang ở trên Windows, tôi có thể khuyên bạn nên cài đặt g++và Boost thông qua MSYS2 . Tôi đã viết một hướng dẫn tốt đẹp về cách cài đặt MSYS2. Sau khi làm theo hướng dẫn, bạn có thể cài đặt Boost bằng cách sử dụng 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;
}

Trên máy của tôi, nó chạy trong 4,8 giây cho 1907000000 (1.9e9).

Đoạn mã trên được gửi lại từ thư viện C ++ cá nhân của tôi , vì vậy tôi đã bắt đầu.

Thời gian chính thức

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. Đó là nhanh chóng. Máy của bạn là gì?
Addison Crump

@VoteToC Đóng Intel i5-4670k chạy Windows 64 bit 7.
orlp

quan tâm để thêm một lời giải thích?
Liam

@Liam Nó chỉ là một cái rây có bất kỳ số nào là bội số của 2 và 3 bên ngoài sàng.
orlp

3

C ++, 2.47215s (ngày 29 tháng 2 năm 2016)

Đây là một phiên bản đa luồng (cẩu thả) của câu trả lời khác của tôi.

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

Sử dụng một rây Eratosthenes được phân đoạn với hệ số bánh xe là 6 để bỏ qua tất cả các bội số của 2/3. Làm cho việc sử dụng POSIX ffsllđể bỏ qua các giá trị tổng hợp liên tiếp.

Để biên dịch:

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

thời gian không chính thức

Được hẹn giờ với Intel i5-6600k trên Ubuntu 15.10, trường hợp 1907000000 đã diễn ra 0.817s.

Thời gian chính thức

Để có được thời gian chính xác hơn, tôi đã tính thời gian này 100 lần, sau đó chia thời gian cho 100 lần.

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

Vì câu trả lời của con trăn này và @Dennis rất gần, tôi có thể trả lại chúng để có kết quả chính xác hơn.
Liam

Tuyệt tuyệt tuyệt. Điều này thậm chí còn ít ý nghĩa với tôi hơn so với CJam hoặc Pyth. Tôi sẽ gọi nó là quái vật thay đổi bit! +1
Tamoghna Chowdhury 3/03/2016

Bên cạnh đó, bạn có thể thử CUDA / OpenCL để tăng tốc GPU không? Nếu tôi biết nhiều C hơn, tôi có thể có.
Tamoghna Chowdhury 3/03/2016

Vâng, tôi đoán rằng tôi đã hơi quá với bithifting / masking: PI không biết liệu GPGPU có hữu ích ở đây hay không; khu vực duy nhất tôi có thể thấy nó có thể giúp đỡ là trong các số nguyên tố nhỏ trước và thậm chí sau đó tốc độ truyền dữ liệu có thể đủ để giết chết nó. Điều vẫn còn làm tôi bực mình là tôi vẫn bị giảm 10% so với lần thực hiện sàng nhanh nhất mà tôi từng thấy
hellowworld922 ngày

2

C, 2m42.7254s (ngày 28 tháng 2 năm 2016)

Lưu dưới dạng pi.c, biên dịch thành gcc -o pi pi.c, chạy như ./pi <arg>sau:

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

Cần rất nhiều bộ nhớ để chạy! Nếu phần cứng của bạn không thể tiết kiệm tới hai gigabyte bộ nhớ thực, thì chương trình sẽ bị sập hoặc chạy rất chậm do bị đập VMM và HD.

Thời gian gần đúng trên phần cứng của tôi là 1.239 × 10 -8 · n 1.065 s. Ví dụ: đầu vào n = 2 × 10 9 mất khoảng 100 giây để chạy.

Thời gian chính thức

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

Điều này làm việc bằng cách sử dụng sàng của eratosthenes? Tôi sẽ đến lúc về nhà
Liam

Tôi đang phân tách trường hợp đầu tiên (những người khác chạy tốt). Nó xảy ra sau ~ 1 phút thời gian chạy. Tôi đã thêm if (p==NULL) {exit(1);}dòng vào mã, vì vậy tôi không tin malloc bị lỗi (cũng sẽ thất bại ngay từ đầu, không phải 1 phút sau). Ý tưởng về những gì đang xảy ra?
Liam

Nhiều hệ thống, bao gồm cả Linux, phân bổ lạc quan. Ví dụ: nếu bạn yêu cầu 1 Gb, nó sẽ "cung cấp" cho bạn, nhưng khi bạn thực sự sử dụng nó và nếu hệ thống không thể tìm thấy nó, nó sẽ bị sập. Nếu đây là trường hợp, nó có khả năng sẽ bị rơi ở bộ nhớ. Giây phút đó là thời gian dành để cố gắng kết hợp đống thành một khối liền kề. Ngoài ra, hãy kiểm tra hệ thống của bạn có sizeof (bool) == 1. Nếu là == 4, thì tôi có thể viết lại cái này để sử dụng char.

Tôi đã kiểm tra rồi. Bool là 1 byte. Có thể chỉ yêu cầu mẫu 2 * 10 ^ 9 byte bộ nhớ trong ngăn xếp? Tức là khai báo một biến toàn cục (trên gcc) tôi tin rằng sẽ được bắt đầu thành 0. Điều này sẽ yêu cầu sử dụng charthay vì mặc dù tôi nghĩ.
Liam

1
@Liam Khó nói. Tràn số nguyên đã ký là hành vi không xác định, do đó, không nhìn vào tập hợp được tạo, thật khó để dự đoán trình biên dịch đã làm gì.
Dennis

2

Julia, 1m 21.1329

Tôi muốn đưa ra một cái gì đó nhanh hơn một chút, nhưng bây giờ đây là một triển khai khá ngây thơ của Sàng của 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))

Nhận phiên bản mới nhất của Julia cho hệ thống của bạn ở đây . Hãy chắc chắn rằng thực thi Julia là trong con đường của bạn. Lưu mã dưới dạng sieve.jlvà chạy từ dòng lệnh như julia sieve.jl N, Nđầu vào ở đâu.

Thời gian chính thức

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
Tôi đã triển khai Sàng Atkin và việc thực hiện của tôi chậm hơn. >: U
Alex A.

@Liam Whoa. Tôi tự hỏi tại sao thời gian chính thức dài hơn so với thời gian không chính thức của tôi. Thời gian chính thức là khá khủng khiếp.
Alex A.

Vâng thời gian chính thức là cho tất cả các trường hợp điểm số với nhau. Những người không chính thức đi số theo số. Ngoài ra, máy tính của tôi có thể không nhanh như của bạn.
Liam

@Liam ơi, điều đó có ý nghĩa hơn. Dang, tôi nghĩ rằng điều này là đàng hoàng. Oh tốt, trở lại bảng vẽ.
Alex A.

Tôi sắp đánh cắp thuật toán của Dennis ... chỉ để tôi có thể hiểu nó nhanh như thế nào.
Liam

2

Java, 42.663122s (ngày 3 tháng 3 năm 2016)

* điều này đã được chương trình hẹn giờ trong nội bộ (mặc dù trên máy tính của 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);
}
}

Theo các truyền thống PPCG tuyệt vời về mã tự viết tài liệu (mặc dù không phải theo nghĩa đen: p).

Điều này là để chứng minh rằng Java có thể đủ nhanh để cạnh tranh với các ngôn ngữ VM khác khi sử dụng các thuật toán tương tự.

Chạy thông tin

Chạy nó như bạn sẽ có câu trả lời của @ CoolestVeto, nhưng tôi không cần đối số dòng lệnh, nó có thể lấy chúng từ STDIN.

Tinh chỉnh NUM_THREADShằng số để đặt nó thành gấp đôi số lõi của bạn để có hiệu suất tối đa (như tôi đã quan sát - Trong trường hợp của tôi, tôi có 8 lõi ảo nên được đặt thành 16, OP có thể muốn 12 cho bộ xử lý lõi hexa của mình).

Khi tôi chạy các thử nghiệm này, tôi đã sử dụng JDK 1.7.0.45 với BlueJ 3.1.6 (IntelliJ đang cập nhật) trên Windows 10 Enterpise x64 trên máy tính xách tay ASUS K55VM (Core i7 3610QM, RAM 8GB). Google Chrome 49.0 64 bit với 1 tab (PPCG) mở và tải xuống 1 tệp QBittorrent đang chạy trong nền, sử dụng 60% RAM khi bắt đầu chạy.

Về cơ bản,

javac PrimeCounter.java
java PrimeCounter

Chương trình sẽ đưa bạn qua phần còn lại.

Thời gian được thực hiện bởi Java sẵn có System.nanoTime().

Chi tiết thuật toán:

Có 3 biến thể cho các trường hợp sử dụng khác nhau - một phiên bản ngây thơ như @ CoolestVeto (nhưng đa luồng) cho các đầu vào dưới 2 ^ 15, và một sàng Eratosthenes bitmasked với loại bỏ lẻ cho các đầu vào trên 2 ^ 28 và một sàng Eratosthenes bình thường với một 2/3/5/7 hệ số bánh xe để loại bỏ bội số.

Tôi sử dụng sàng bitmasked để tránh các đối số JVM đặc biệt cho các trường hợp thử nghiệm lớn nhất. Nếu điều đó có thể được thực hiện, chi phí cho việc tính toán số lượng trong phiên bản bitmasked có thể được loại bỏ.

Đây là đầu ra:

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

Chỉ xuất ra kết quả của pi (n) (không có lời nhắc) có thể tiết kiệm thời gian, vì STDOUT là ... tốt, giả sử nó có thể nhanh hơn một chút.
dùng48538

@ zyabin101, nếu bất kỳ ai có đủ kiên nhẫn để duyệt mã, anh ấy / cô ấy sẽ hiểu rằng độ trễ STDOUT đã được tính đến.
Tamoghna Chowdhury

Cũng để tính thời gian, tôi đã gửi thiết bị xuất chuẩn đến / dev / null
Liam

@Liam Tôi đoán bạn sẽ phải tạo một ngoại lệ trong trường hợp của tôi. Bạn có thể điều chỉnh phương thức chính cho các đối số dòng lệnh, nhưng dù sao chương trình cũng tự định thời gian. Kiểm tra xem sao. Xin vui lòng?
Tamoghna Chowdhury 3/03/2016

Dĩ nhiên tôi sẽ. Tôi sẽ làm nó vào ngày mai. Nếu tôi gặp sự cố, tôi sẽ ping bạn trong trò chuyện
Liam

2

Con trăn 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)

Sử dụng sàng của Eratosthenes. Chạy ở mức trung bình của 8.775snơi n = 10^7. Theo thời gian, tôi đã sử dụng timelệnh dựng sẵn . Ví dụ:

$ time python3 test.py 90
24

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

Đó là cái sàng! Tôi không thể sử dụng điều này trong Java bởi vì nó không giống như một mảng boolean được sử dụng. D:
Addison Crump

lỗi bộ nhớ trong các trường hợp lớn hơn.
Liam

Những trường hợp nào? Tôi tin rằng tôi đã sửa nó. @Liam
Zach Gates

2
@VoteToC Đóng Sau đó, không sử dụng mảng Boolean. Sử dụng một mảng số nguyên và dịch chuyển / mặt nạ bit, với mỗi bit đại diện cho một giá trị Boolean.
mbomb007

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

1

C ++, 9.321 (ngày 29 tháng 2 năm 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';
}

Sử dụng một rây Eratosthenes được phân đoạn với hệ số bánh xe là 6 để bỏ qua tất cả các bội số của 2/3. Làm cho việc sử dụng POSIX ffsllđể bỏ qua các giá trị tổng hợp liên tiếp.

Có khả năng có thể được tăng tốc bằng cách làm cho sàng phân đoạn hoạt động song song.

Để biên dịch:

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

thời gian không chính thức

Được hẹn giờ với Intel i5-6600k trên Ubuntu 15.10, trường hợp 1907000000 đã diễn ra 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

Thời báo chính thức

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.