최대 n까지 소수의 수를 계산


64

π ( N ) 소수의 수보다 작거나 같은지 N .

입력 : 자연수, n은 .

출력 : π (n).

채점 : 이것은 도전입니다. 점수는 점수 사례의 시간 합계입니다. 컴퓨터에있는 각 항목의 시간을 정합니다.

규칙 및 세부 사항

  • 코드는 작동한다 n 개의 20 억 (2,000,000,000)까지.

  • 이를 사소한 내장 기능은 허용되지 않습니다. 여기에는 내장 된 π 함수 또는 π ( n ) 의 값 목록이 포함됩니다 .

  • 우선 순위를 테스트하거나 소수를 생성하는 내장 기능은 허용되지 않습니다. 여기에는 다음 글 머리 기호를 제외하고는 외부 또는 하드 코딩되지 않은 소수 목록이 포함됩니다.

  • 최대 19까지의 소수를 하드 코딩 할 수 있습니다.

  • π 구현은 결정 론적이어야합니다. 이것은 특정 n 이 주어지면 코드가 거의 같은 시간에 실행되어야 함을 의미합니다 .

  • 사용되는 언어는 Linux (Centos 7)에서 자유롭게 사용할 수 있어야합니다. 코드 실행 방법에 대한 지침이 포함되어야합니다. 필요한 경우 컴파일러 / 인터프리터 세부 사항을 포함하십시오.

  • 공식 시간은 내 컴퓨터에서옵니다.

  • 게시 할 때 코드 실행 속도를 예상 할 수 있도록 일부 / 모든 테스트 / 점수 사례에 자체 측정 시간을 포함 시키십시오.

  • 제출물은이 질문에 대한 답변 게시물에 적합해야합니다.

  • 64 비트 centos7을 실행 중입니다. 8GB의 RAM과 1GB의 스왑 만 있습니다. CPU 모델은 AMD FX (TM) -6300 6 코어 프로세서입니다.

테스트 사례 ( 소스 ) :

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

점수 사례 ( 동일한 출처 )

평소와 같이 이러한 경우는 변경 될 수 있습니다. 채점 사례에 대한 최적화는 허용되지 않습니다. 또한 합리적인 실행 시간과 정확한 결과의 균형을 맞추기 위해 사례 수를 변경할 수도 있습니다.

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

지속

이것은 챌린지이며 컴퓨터에서 항목을 실행해야하므로 2 주 후에 항목 타이밍을 중지 할 권한이 있습니다. 이 시점 이후에도 출품작은 접수되지만 공식적으로 출품되었다는 보장은 없습니다.

이것을 말하면서, 나는이 도전에 대한 답변이 너무 많을 것으로 기대하지 않으며 새로운 답변을 무기한으로 계속 계속 할 것입니다.

채점 세부 사항

다음 스크립트를 사용하여 더 빠른 항목을 작성했습니다.

#!/bin/bash

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

echo DennisC
exec 2>> times/dennisc.txt
time for j in ${a[@]}; do ./dennisc $j; done >> /dev/null;

echo DennisPy
exec 2>> times/dennispy.txt
time for j in ${a[@]}; do pypy dennispy.py <<< $j; done >> /dev/null;

echo arjandelumens
exec 2>> times/arjandelumens.txt
time for j in ${a[@]}; do ./arjandelumens $j; done >> /dev/null;

echo orlp
exec 2>> times/orlp.txt
time for j in ${a[@]}; do ./orlp $j; done >> /dev/null;

# echo mwr247
# time node-v4.3.1-linux-x64/bin/node mwr247.js

# mwr247 using js seems a bit longer, so I am going to run the fastest
# and then come back to his. 

# mwr247 provided a function, so I appended
# console.log( F( <argument> ) )
# to his code, for each argument.

time에 쓰기 때문에을 사용하여 로그 파일로 stderr보냈습니다 . 로 전송 된 것을 알 수 있습니다 . 프로그램이 올바른 출력을 생성하고 있음을 이미 확인했기 때문에 이것은 문제가되지 않습니다.stderrexec 2 >> <filename>stdout/dev/null

위의 timeall.sh스크립트를 사용하여 10 번 실행했습니다.for i in {1..10}; do ./timeall.sh; done;

그런 다음 real time각 항목 의 점수를 평균했습니다 .

타이밍 동안 내 컴퓨터에서 다른 프로그램이 실행되고 있지 않았습니다.

또한 공식 시간이 각 항목에 추가되었습니다. 자신의 평균을 다시 확인하십시오.


pi (n)의 처음 2e9 값으로 조회 테이블을 사용할 수없는 이유는 무엇입니까? 괜찮을까요? (큰 테이블 일 것이기 때문에 얼마나 빠를 지 확실하지 않습니다)
Luis Mendo

@DonMuesli 받아 들일 수없는 (도전의 정신에 위배됨), 나는 지금도 명백히 금지되도록 편집했습니다.
Liam

8
도전의 "정신"을 말하는 것은 위험합니다. 당신의 "정신에 대한"다른 사람의 "큰 트릭"일 수 있습니다 :-) 그것을 명시 적으로 만드는 것이 좋습니다
Luis Mendo

1
내장이란 무엇입니까? 라이브러리에 주요 목록 기능이 있습니다. 사용해도 될까요? 그렇지 않은 경우 프로그램에서 라이브러리의 소스 코드를 복사하여 사용할 수 있습니까?
nimi

1
@Liam : 예, 알고 있지만 내장 된 것으로 간주되는 것은 무엇입니까? 라이브러리에서 소스 코드를 복사하는 기능이 내장되어 있습니까?
nimi

답변:


119

C, 0.026119 초 (2016 년 3 월 12 일)

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

이것은 Meissel-Lehmer 방법을 사용합니다 .

타이밍

내 컴퓨터 에서 결합 된 테스트 사례에 대해 약 5.7 밀리 초가 되었습니다. 이것은 1867MHz DDR3 RAM이있는 Intel Core i7-3770에 있으며 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

때문에 분산이 너무 가지고 , 나는 비공식 실행 시간에 대한 프로그램 내에서 타이밍을 사용하고 있습니다. 이것은 결합 된 런타임의 평균을 계산 한 스크립트입니다.

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

공식 시간

이번에는 스코어 케이스를 1000 번 수행합니다.

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

작동 원리

공식

를 양의 정수라고 하자 .x

각 양의 정수 는 다음 조건 중 하나를 정확히 만족시킵니다.nx

  1. n=1

  2. p [ 1 , 3 n 은 에서 소수 로 나눌 수 있습니다.p[1,x3]

  3. p q ( 3 n=pq , 와 (반드시 구별되지 않음)에있는 소수 .pq(x3,x23)

  4. n > 3 n 은 소수이고n>x3

하자 소수의 수가 나타내는 되도록 . 네 번째 범주 에는 숫자가 있습니다.p p y π ( x ) π ( 3 π(y)ppyπ(x)π(x3)

하자 양의 정수를 나타낸다 량 정확하게의 산물 아닌 제 중 소수를 소수를. 거기 세 번째 카테고리에 분류 번호.Pk(y,c)mykcP2(x,π(x3))

마지막으로, 는 첫 번째 소수 에 대해 공동 소수 인 양의 정수 의 양을 나타냅니다 . 거기 제 카테고리에 속하는 번호.ϕ(y,c)kycxϕ(x,π(x3))

모든 카테고리에 숫자 가 있으므로x

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

따라서,

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

및 가 필요한 경우 세 번째 범주의 숫자는 고유 한 표현을 갖습니다 . 이런 식으로 프라임 와 의 곱은 인 경우에만 세 번째 범주 에 있으므로 에 대한 가능한 값 의 고정 값 및 여기서 는 소수를 나타냅니다.pqpxpqx3<pqxpπ(xp)π(p)+1qpP2(x,π(x3))=π(x3)<kπ(x)(π(xpk)π(pk)+1)pkkth

마지막으로, 첫 소수 와 동일 하지 않은 모든 양의 정수 는 로 고유 한 방식으로 표현 될 수 있습니다 . 여기서 는 의 가장 작은 소수입니다 . 이런 식으로 , 는 첫 번째 소수에 대한 코 프라임입니다.nycn=pkfpknkcfk1

이것은 재귀 공식 집니다. 특히 이면 합은 비어 있으므로 입니다.ϕ(y,c)=y1kcϕ(ypk,k1)c=0ϕ(y,0)=y

이제 첫 번째 소수 (백만 대 수십억) 만 생성하여 를 계산할 수있는 공식이 있습니다 .π(x)π(x23)

연산

를 계산해야합니다 . 여기서 는 만큼 낮아질 수 있습니다 . 이를 수행하는 다른 방법이 있지만 (공식을 재귀 적으로 적용하는 것과 같이) 가장 빠른 방법은 모든 소수를 까지 열거하는 것 같습니다 . 이것은 에라토스테네스의 체로 수행 할 수 있습니다.π(xp)px3x23

먼저 모든 소수를 식별하고 에 저장하고 및 를 동시에 계산합니다. 그런 다음 에서 모든 에 대해 를 계산하고 각 연속 몫까지의 소수를 계산합니다. .[1,x]π(x3)π(x)xpkk(π(x3),π(x)]

또한 은 닫힌 , 어느 의 계산을 완료 할 수 있습니다 .π(x3)<kπ(x)(π(pk)+1)π(x3)π(x))(π(x3)+π(x)12P2(x,π(x3))

알고리즘의 가장 비싼 부분 인 의 계산을 남깁니다 . 단순히 재귀 수식을 사용하면 를 계산하기 위해 함수 호출 이 필요합니다 .ϕ2cϕ(y,c)

우선, 의 모든 값에 대한 , 그래서 . 그 자체만으로도이 관측으로 계산이 가능해집니다. 이것은 이하의 숫자 가 10 개의 고유 소수의 곱보다 작기 때문에 압도적 인 대다수의 소환사가 사라지기 때문입니다.ϕ(0,c)=0cϕ(y,c)=y1kc,pkyϕ(ypk,k1)2109

또한 와 정의의 첫 번째 요약을 그룹화 하면 대체 공식 . 따라서 고정 및 적절한 값에 대해 사전 계산 를 수행 하면 나머지 함수 호출과 관련 계산이 대부분 절약됩니다.ycϕϕ(y,c)=ϕ(y,c)c<kc,pkyϕ(ypk,k1)ϕ(y,c)cy

만약 후 에서 정수 보낸 없음의 배수임을 는 정확히 와 합니다. 또한 이므로 .mc=1kcpkϕ(mc,c)=φ(mc)[1,mc]p1,,pcmcgcd(z+mc,mc)=gcd(z,mc)ϕ(y,c)=ϕ(ymcmc,c)+ϕ(y

오일러의 고문 함수는 곱셈이므로 이며 가장 쉬운 방법은 유도하도록 모두 만의 값을 미리 계산하여 의 .φ(mc)=1kcφ(pk)=1kc(pk1)ϕ(y,c)yy[0,mc)

또한 설정 하면 습니다. Lehmer의 논문에서 원래 정의. 이것은 우리에게 미리 계산하는 간단한 방법 제공 값의 증가에 대한 .ϕ ( y , c ) = ϕ ( y , c - 1 ) -c=c1ϕ(y,c)ϕ(y,c)=ϕ(y,c1)ϕ(ypc,c1)ϕ(y,c)c

사전 계산에 대한 또 의 어느 낮은 값 , 우리는 낮은 값을 미리 계산하는 것이다 이 소정 임계치 아래로 떨어지는 후 짧은 순환을 절단.c yϕ(y,c)cy

이행

이전 섹션에서는 코드의 대부분을 다룹니다. 남아있는 중요한 세부 사항 중 하나는 함수의 나누기 Phi가 수행되는 방법입니다.

계산 하려면 첫 번째 소수 로만 구분 하면되므로 대신 함수를 사용할 수 있습니다 . 단순히 를 소수 나누는 대신 , 에 를 곱하고 대신 를 . x64 에서 정수 곱셈이 구현되는 방식 때문에 로 나누지 않아도됩니다. 의 상위 64 비트는 자체 레지스터에 저장됩니다.πϕypydpπ(x3)fastdivypydp264pyp 264dpy264264dpy

이 방법은 사전 계산 필요로 하며 이는 직접 계산하는 것보다 빠르지 않습니다 . 그러나 동일한 소수로 반복해서 나누고 나누는 것보다 나누기가 훨씬 느리기 때문에 중요한 속도 향상이 발생합니다. 이 알고리즘에 대한 자세한 내용과 공식적인 증거는 곱셈을 사용하는 Invariant Integers의 Division 에서 찾을 수 있습니다 .dpyp


22
하나는 단순히 데니스를 능가하지 않습니까?
애디슨 크럼

8
솔직히 나는 이것이 얼마나 빠른지 믿을 수 없습니다. 나는 무슨 일이 일어나고 있는지 이해할 시간이 없었지만 실제로는 필요합니다.
Liam

27
@Liam 나는 이것이 어떻게 작동하는지 완전히 설명하려고하지만 여전히 속도를 높이려고합니다. PPCG에 LaTeX가 있었으면 좋겠다.
Dennis

15
재미있는 메모 : (내 컴퓨터에서) 이것은 현재 github의 소수 C ++ 라이브러리에서 Mathematica의 내장 및 kimwalisch를 치고 있지만 현재 유일한 항목입니다.
Michael Klein

10
@TheNumberOne은 그것에 대해 말하지 않습니다 ... 다른 사람들이 그를 이길 필요가있을 수 있습니다
Liam

24

C99 / C ++, 8.9208 초 (2016 년 2 월 28 일)

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

비트 맵 기반의 소거 시체 구현. 다음 단계를 수행합니다.

  1. 먼저 2,3,5,7의 배수를 포함하는 체를 채울 반복 비트 패턴을 생성하십시오.
  2. 그런 다음, sieve 방법을 사용하여 sqrt (n)보다 작은 모든 소수 배열을 생성하십시오.
  3. 다음으로 이전 단계의 프라임 목록을 사용하여 체에 씁니다. 이것은 대략 L1 캐시 크기 인 체 덩어리에서 수행되므로 체 처리가 L1 캐시를 지속적으로 스 래시하지 않습니다. 이것은 청킹이 아닌 것보다 5 배 빠른 속도를 제공하는 것으로 보입니다.
  4. 마지막으로 비트 수를 수행하십시오.

gcc primecount.c -O3 -lm -Walli7-4970k에서 우분투 15.10 (64 비트)으로 컴파일 하고 실행하면 전체 점수 사례에 약 2.2 초가 걸립니다. 실행 시간은 3 단계가 지배합니다. 청크는 독립적이기 때문에 원하는 경우 멀티 스레딩 될 수 있습니다. 청크 경계가 적절하게 정렬되도록주의를 기울여야합니다.

체에 엄격히 필요한 것보다 약간 더 많은 메모리를 할당합니다. 이는 버퍼 끝 오버런을위한 공간을 제공하며, 이는 3 단계의 루프 언 롤링이 제대로 작동하는 데 필요합니다.

공식 시간

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
A의 프로그래밍 퍼즐 및 코드 골프, 축하에 오신 것을 환영 별의 첫 번째 게시물!
Dennis

사용을 고려하십시오 -O3 -march=native. CPU는 popcnt명령어를 지원 하며 컴파일러는 때때로 순수한 C 구현을 인식하고 단일 명령어로 컴파일 할 수 있습니다. 또는 __builtin_popcountllDennis의 답변과 같이 GNU C 에서 사용 하는 것이 좋습니다 .
Peter Cordes

-march=nativeHaswell CPU에서 BMI2를 활성화하면보다 효율적인 가변 카운트 시프트 명령이 가능합니다. ( SHLX 대신 기존 SHL 의 수를 필요합니다 cl.) 영업 이익의 AMD 파일 드라이버 CPU는 BMI2을 가지고 있지 않지만, popcnt을 가지고있다. 그러나 AMD CPU는 Intel CPU보다 가변 카운트 SHL을 더 빨리 실행하므로 튜닝하는 동안 BMI2로 컴파일하는 것이 여전히 적절할 수 있습니다. 파일 드라이버는 마이크로 최적화가 진행되는 한 Haswell과는 상당히 다르지만 요청하는 -march=native것이 좋습니다
Peter Cordes

12

Python 2 (PyPy 4.0), 2.36961 초 (2016 년 2 월 29 일)

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

이것은 Meissel-Lehmer 방법을 사용합니다.

타이밍

$ 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

공식 시간

비슷한 시간에 다른 답변이 있었으므로 더 정확한 결과를 얻었습니다. 이걸 100 번 했어요 점수는 다음 시간을 100으로 나눈 값입니다.

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

5
또한 참고 사항 :이 코드는 내 코드보다 15,102.4 배 빠릅니다. +1
Addison Crump

12

Java, 이 시스템 에서 25,725.315 초

이것은 이길 수 없으며 , 체를 사용하지 않는 답변을 게시하고 싶었습니다.

업데이트 : 이것은 현재 최고 점수보다 약 150,440.4386 배 느린 순위입니다. 그들에게 대답해라, 그들의 대답은 굉장하다.

바이트 코드 :

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

소스 코드:

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

실제로 옵티마이 저가 걸리는 시간이 길어졌습니다. >.> 젠장.

1000 미만의 입력은 내 컴퓨터에서 평균 시간이 .157s 인 것으로 보입니다 (예 : 클래스로드 ಠ_ಠ). 약 1e7을 지나면 까다로워집니다.

타이밍 목록 :

> 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는 현재 일관된 100 % CPU에서 현재 실행 중입니다. 이것은 완전히 효율적입니다. 무슨 소리 죠?
애디슨 크럼

java (How to C / C ++> java)에 대한 튜토리얼을 나에게 줄 수 있습니까? 나는 javac voteToClose.java(클래스 이름을 바꿨다)로 컴파일 한 다음 무엇을?
Liam

@Liamjava voteToClose <input>
애디슨 크럼프

1
잠깐만 ... 왜 바이트 코드가 말해 cafe babe?
Cyoce

12
@Cyoce 모든 Java 클래스 파일은 0xCAFEBABE로 시작됩니다.
애디슨 크럼프

8

녹, 0.37001 초 (2016 년 6 월 12 일)

Dennis의 C답변 보다 약 10 배 느리지 만 Python 항목보다 10 배 빠릅니다. 이 답변은 @Shepmaster와 @Veedrac이 Code Review 에서 개선하는 데 도움을주었습니다 . @ Veedrac 's post 에서 그대로 사용됩니다 .

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
}

시간 : time ./time.sh어디에서 time.sh보이는지 :

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

출력은 다음과 같습니다.

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

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

8

Node.js (JavaScript / ES6), 83.549s (2016 년 11 월 11 일)

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)

마지막 으로이 문제를 해결하기 위해 돌아 왔으며 이전보다 더 작고 간단하며 훨씬 빠릅니다. 더 느린 무차별 대입 방법이 아니라보다 효율적인 데이터 구조와 함께 에라토스테네스 시브 (Sieve of Eratosthenes)를 활용하여, 실제로 인터넷에서 찾을 수있는 한, 가장 빠른 JS 소수입니다. 기능).

일부 데모 시간 (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

+=1그렇지 ++않습니까?
ETHproductions

@ETHproductions 사전 또는 사후 증가를 의미하는지 여부에 따라 다릅니다. i++다른 규모의 운영에 대한 가치 변화를 유지해야합니다. 사전 증분을 테스트하지는 않았지만와 거의 같을 것으로 의심됩니다 +=1.
Mwr247

그러나 메모리 +=1에 할당해야합니다 1. 내 생각에 내가 당신이라면을 사용 ++i합니다. 값을 증가시키는 단일 명령이 있다고 생각하므로 확실하지 않습니다.
Ismael Miguel

왜 그렇게 응축되어 있습니까? 이것은 code-golf 가 아니며 실제로 읽기가 어렵습니다.
Cyoce

또한, 변경하는 데 도움이 수도 (...)|0;i=0(...)|(i=0)
Cyoce

6

C ++ 11, 22.6503s (2016 년 2 월 28 일)

로 컴파일하십시오 g++ -O2 -m64 -march=native -ftree-vectorize -std=c++11 numprimes.cpp. 이 옵션은 중요합니다. 또한 Boost가 설치되어 있어야합니다. 우분투에서는 이것을 설치하여 사용할 수 있습니다 libboost-all-dev.

Windows를 사용하는 경우 MSYS2를g++ 통한 설치 및 부스트를 권장 할 수 있습니다 . MSYS2를 설치하는 방법에 대한 훌륭한 자습서 를 작성 했습니다 . 튜토리얼을 따른 후를 사용하여 Boost를 설치할 수 있습니다 .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;
}

내 컴퓨터에서 1907000000 (1.9e9) 동안 4.8 초 안에 실행됩니다.

위의 코드는 개인 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

: 오 Dayyyum. 빠릅니다. 당신의 기계는 무엇입니까?
애디슨 크럼

@VoteToClose 64 비트 Windows 7을 실행하는 Intel i5-4670k
orlp

설명을 추가 할까?
Liam

@Liam 그것은 체에서 남은 2와 3의 배수 인 숫자를 가진 체입니다.
orlp

3

C ++, 2.47215 초 (2016 년 2 월 29 일)

이것은 내 다른 대답의 (조잡한) 멀티 스레드 버전입니다.

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

에라토스테네스의 분할 체를 6의 휠 인수 분해로 사용하여 2/3의 모든 배수를 건너 뜁니다. POSIX ffsll를 사용하여 연속적인 복합 값을 건너 뜁니다.

컴파일하기:

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

비공식 타이밍

Ubuntu 15.10에서 Intel i5-6600k를 사용하여 1907000000 사건이 발생했습니다 0.817s.

공식 시간

더 정확한 시간을 얻으려면이 시간을 100 번 지정한 다음 시간을 100으로 나눕니다.

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

이것과 @Dennis의 python 답변이 너무 가깝기 때문에 더 정확한 결과를 위해 시간을 다시 지정할 수 있습니다.
Liam

와우 와우 와우 이것은 CJam이나 Pyth보다 나에게 의미가 없습니다. 나는 그것을 비트 시프트 몬스터라고 부를 것이다! +1
Tamoghna Chowdhury

따로, GPU 속도 향상을 위해 CUDA / OpenCL을 사용해 볼 수 있습니까? 더 많은 C를 안다면 나는 가지고 있을지도 모른다.
Tamoghna Chowdhury

예, 비트 시프 팅 / 마스킹이 약간 과도하다고 생각합니다. PI는 GPGPU가 도움이 될지 여부를 모릅니다. 내가 도울 수있는 유일한 영역은 작은 소수를 미리 체포하고 있었고 심지어 데이터 전송 속도로도 충분하지 않을 수 있습니다. 무엇 아직도 날 부랑자 것은 내가 10 정도의 비율로 떨어져 여전히 해요 있다는 것입니다 내가 본 가장 빠른 체 구현
helloworld922

2

C, 2m42.7254s (2016 년 2 월 28 일)

다른 이름으로 저장하고 pi.c컴파일하고 gcc -o pi pi.c다음과 같이 실행하십시오 ./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;
}

실행하려면 많은 메모리가 필요합니다! 하드웨어가 최대 2GB의 실제 메모리를 절약 할 수없는 경우 VMM 및 HD 스 래싱으로 인해 프로그램이 충돌하거나 매우 느리게 실행됩니다.

내 하드웨어의 대략적인 타이밍은 1.239 × 10 -8 · n 1.065 초입니다. 예를 들어, n = 2 × 10 9 의 입력은 실행하는 데 약 100 초가 걸립니다.

공식 시간

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

이것은 에라토스테네스의 체를 사용하여 작동합니까? 집에 도착하면 시간을 내주십시오
Liam

첫 번째 경우에 segfaulting하고 있습니다 (다른 사람들은 잘 실행됩니다). ~ 1 분의 런타임 후에 발생합니다. if (p==NULL) {exit(1);}코드 에 줄을 추가 했기 때문에 malloc이 실패한다고 생각하지 않습니다 (1 분이 아니라 처음에 실패 할 것입니다). 무슨 일이 일어나고 있는지에 대한 아이디어?
Liam

Linux를 포함한 많은 시스템은 낙관적 할당을 수행합니다. 예를 들어 1Gb를 요청하면 "Gb"를 제공하지만 실제로 사용하려고 할 때 시스템에서 찾을 수 없으면 충돌이 발생합니다. 이 경우 memset에서 충돌이 발생했을 수 있습니다. 소요되는 시간은 힙을 연속 블록으로 통합하는 데 소요되는 시간입니다. 또한 시스템에서 sizeof (bool) == 1인지 확인하십시오. == 4이면 char을 사용하도록 다시 작성할 수 있습니다.

나는 이미 확인했다. Bool은 1 바이트입니다. 스택에 2 * 10 ^ 9 바이트의 메모리를 요청할 수 있습니까? 즉, (gcc에서) 0으로 시작될 것으로 생각되는 전역 변수를 선언하십시오 char. 생각하지만 대신 사용해야합니다 .
Liam

1
@Liam 말하기 어렵다. 부호있는 정수 오버플로는 정의되지 않은 동작이므로 생성 된 어셈블리를 보지 않으면 컴파일러가 수행 한 작업을 예측하기가 어렵습니다.
Dennis

2

줄리아, 1 분 21.1329 년대

나는 조금 더 빠른 것을 제안하고 싶지만 지금은 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))

시스템에 맞는 최신 버전의 Julia를 다운로드 하십시오 . Julia 실행 파일이 경로에 있는지 확인하십시오. 같은 코드를 저장 sieve.jl하고 명령 줄 등으로부터 실행 julia sieve.jl N, N입력입니다.

공식 시간

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
Atkin의 체를 구현했으며 그 구현이 느립니다. > : U
Alex A.

@ 리암 우와. 왜 공식 시간이 비공식 시간보다 길어질 지 궁금합니다. 공식 시간은 꽤 끔찍합니다.
Alex A.

공식 시간은 모든 점수 사건에 대한 것입니다. 비공식적 인 사람들은 숫자로 이동합니다. 또한 내 컴퓨터는 아마도 당신 컴퓨터만큼 빠르지 않을 것입니다.
Liam

@Liam 아, 더 이해가 되네요. Dang, 나는 이것이 괜찮다고 생각했다. 오, 다시 그리기 보드로 돌아갑니다.
Alex A.

데니스 알고리즘을 훔치려 고하는데 ... 속도가 얼마나 빠른지 이해할 수 있습니다.
Liam

2

Java, 42.663122s * (2016 년 3 월 3 일)

* 이것은 프로그램에 의해 내부적으로 시간이 정해졌습니다 (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);
}
}

자체 문서화 코드의 위대한 PPCG 전통을 따릅니다 (문자 그대로의 의미는 아니지만 : p).

이는 유사한 알고리즘을 사용할 때 Java가 다른 VM 언어와 경쟁하기에 충분히 빠르다는 점을 증명하기위한 것입니다.

런 정보

@CoolestVeto의 답변과 같이 실행하지만 명령 줄 인수가 필요하지 않으므로 STDIN에서 가져올 수 있습니다.

NUM_THREADS최대 성능을 위해 기본 코어 수의 2 배로 설정 하도록 상수를 조정하십시오 (관찰 한 것처럼 8 개의 가상 코어가 있으므로 16으로 설정하면 OP가 그의 헥사 코어 프로세서에 12를 원할 수 있습니다).

이 테스트를 실행했을 때 ASUS K55VM 랩탑 (Core i7 3610QM, 8GB RAM)의 Windows 10 Enterpise x64에서 BlueJ 3.1.6 (IntelliJ가 업데이트 중)과 함께 JDK 1.7.0.45를 사용했습니다. 1 개의 탭 (PPCG)이 열린 Chrome 49.0 64 비트 및 1 개의 파일을 다운로드하는 QBittorrent가 백그라운드에서 실행 중이며 실행 시작시 RAM 사용량은 60 %입니다.

원래,

javac PrimeCounter.java
java PrimeCounter

프로그램이 나머지 과정을 안내합니다.

타이밍은 Java에 내장되어 System.nanoTime()있습니다.

알고리즘 세부 사항 :

2 ~ 15 미만의 입력에 대해서는 @ CoolestVeto 's (그러나 멀티 스레드)와 같은 순진한 버전과 2 ^ 28 이상의 입력에 대해서는 홀수 제거 된 에라토스테네스 (Eratosthenes)의 비트 마스크 체와 2 ^ 28 이상의 입력에 대한 에라토스테네스의 일반 체 배수의 사전 제거를위한 2/3/5/7 휠 인수 분해.

가장 큰 테스트 사례에 대한 특별한 JVM 인수를 피하기 위해 비트 마스크 체를 사용합니다. 이것이 가능하다면, 비트 마스크 된 버전에서의 카운트 계산에 대한 오버 헤드가 제거 될 수있다.

출력은 다음과 같습니다.

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

pi (n)의 결과 만 (프롬프트없이) 출력하면 시간이 절약 될 수 있습니다.
user48538

@ zyabin101, 누군가 코드를 살펴 보는 인내심이 있다면 STDOUT 대기 시간이 고려되었음을 이해할 것입니다.
Tamoghna Chowdhury

또한 타이밍을 위해, 나는 stdout을 / dev / null로 보냈습니다
Liam

@Liam 그렇다면 내 경우에는 예외를 만들어야한다고 생각합니다. 명령 줄 인수에 대한 기본 방법을 조정할 수 있지만 프로그램은 자체 타이밍입니다. 어쨌든 확인하십시오. 부디?
Tamoghna Chowdhury

물론입니다. 내일 할게요 문제가 생기면 채팅에 핑을하겠습니다
Liam

2

파이썬 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)

에라토스테네스의 체를 사용합니다. 평균 8.775s위치 에서 실행됩니다 n = 10^7. 지금까지 내장 time명령을 사용했습니다 . 예를 들면 다음과 같습니다.

$ time python3 test.py 90
24

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

체입니다! 부울 배열이 사용하는 메모리 양이 마음에 들지 않기 때문에 Java에서 이것을 사용할 수 없었습니다. D :
Addison Crump

더 큰 경우에 메모리 오류.
Liam

어떤 경우? 내가 고쳤다 고 생각합니다. @Liam
Zach Gates

2
@VoteToClose 그런 다음 부울 배열을 사용하지 마십시오. 정수 배열과 비트 이동 / 마스킹을 사용하십시오. 각 비트는 부울 값을 나타냅니다.
mbomb007

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

1

C ++, 9.3221 초 (2016 년 2 월 29 일)

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

에라토스테네스의 분할 체를 6의 휠 인수 분해로 사용하여 2/3의 모든 배수를 건너 뜁니다. POSIX ffsll를 사용하여 연속적인 복합 값을 건너 뜁니다.

분할 체를 병렬로 작동시켜 잠재적으로 속도를 높일 수 있습니다.

컴파일하기:

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

비공식 타이밍

Ubuntu 15.10에서 Intel i5-6600k를 사용하여 1907000000 사건이 발생했습니다 2.363s.

41500
4339

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

24850000
1557132

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

40550000
2465109

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

99820000
5751639

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

660000000
34286170

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

1240000000
62366021

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

1337000000
66990613

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

1907000000
93875448

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

공식 시간

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.