Рассчитать количество простых чисел до п


64

π ( n ) - число простых чисел, меньших или равных n .

Ввод: натуральное число, n .

Выход: π (n).

Скоринг: это задача с . Оценка будет суммой раз для случаев оценки. Я буду время каждой записи на моем компьютере.

Правила и детали

  • Ваш код должен работать на п до 2 млрд (2000000000).

  • Встроенные модули, которые упрощают это, не допускаются. Это включает в себя встроенные функции или списки значений для ( n ).

  • Встроенные модули, которые проверяют простоту или генерируют простые числа, не допускаются. Это включает в себя списки простых чисел, которые нельзя искать внешне или жестко закодировать локально, за исключением следующего пункта.

  • Вы можете жестко запрограммировать простые числа до 19 включительно и не более.

  • Ваша реализация π должна быть детерминированной. Это означает, что при заданном n ваш код должен выполняться (примерно) за то же время.

  • Используемые языки должны быть свободно доступны в Linux (Centos 7). Инструкции должны быть включены о том, как запустить ваш код. При необходимости укажите детали компилятора / интерпретатора.

  • Официальные времена будут с моего компьютера.

  • При публикации, пожалуйста, включайте самоизмеримое время в некоторых / всех случаях теста / оценки, просто чтобы дать мне оценку того, насколько быстро работает ваш код.

  • Материалы должны соответствовать ответу на этот вопрос.

  • Я бегу 64bit Centos7. У меня только 8 ГБ оперативной памяти и 1 ГБ подкачки. Модель процессора: AMD FX (TM) -6300 Шестиядерный процессор.

Тестовые случаи ( источник ):

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, поэтому я отправил stderrв файл журнала с помощью exec 2 >> <filename>. Вы можете заметить, что stdoutотправлено /dev/null. Это не проблема, потому что я уже убедился, что программы выдают правильный вывод.

Я запустил вышеуказанный timeall.shскрипт 10 раз, используяfor i in {1..10}; do ./timeall.sh; done;

Затем я усреднил real timeсчет для каждой записи.

Обратите внимание, что на моем компьютере не было запущено никаких других программ во время синхронизации.

Кроме того, официальные времена были добавлены к каждой записи. Пожалуйста, проверьте ваш средний уровень.


Что мешает нам использовать справочную таблицу с первыми 2e9 значениями pi (n)? Это было бы приемлемо? (Не уверен, как быстро это будет, хотя, потому что это будет большой стол)
Луис Мендо

@DonMuesli Это было бы неприемлемо (идет вразрез с духом испытания), я отредактировал, чтобы сделать это прямо сейчас также запрещенным.
Лиам

8
Опасно ссылаться на «дух» вызова. Ваше "против духа" может быть чьей-то еще "большой уловкой" :-) Лучше, чтобы вы сделали это явным
Луис Мендо,

1
Что такое встроенный? У меня есть функция списка простых чисел в библиотеке. Могу ли я использовать это? Если нет, могу ли я скопировать исходный код библиотеки в мою программу и использовать это?
Ним

1
@Liam: Да, я знаю, но что считается встроенным? Является ли копирование исходного кода из библиотеки встроенным?
Ним

Ответы:


119

C 0.026119s (12 марта 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);
}

Здесь используется метод Мейсселя-Лемера .

Задержки

На моей машине я получаю примерно 5,7 миллисекунды для комбинированных тестовых случаев. Это на Intel Core i7-3770 с оперативной памятью DDR3 на частоте 1867 МГц, работающей под управлением 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

Как это устроено

формула

Пусть Икс - положительное целое число.

Каждое натуральное число удовлетворяет ровно одному из следующих условий.NИкс

  1. Nзнак равно1

  2. делится на простое число p в [ 1 , 3 Nп.[1,Икс3]

  3. , где p и q - (не обязательно различающиеся) простые числа в ( 3 Nзнак равнопQпQ.(Икс3,Икс23)

  4. простое и n > 3 NN>Икс3

Пусть обозначает число простых чисел p, таких что p y . Есть π ( x ) - π ( 3 π(Y)ппYчисла, которые попадают в четвертую категорию.π(Икс)-π(Икс3)

Пусть обозначает количество натуральных чисел m y, которые являются произведением ровно k простых чисел, не входящих в число первых c простых чисел. Есть P 2 ( x , π ( 3 пК(Y,с)мYКсномера, которые попадают в третью категорию.п2(Икс,π(Икс3))

Наконец, пусть обозначает количество натуральных чисел k y , которые взаимно просты с первыми c простыми числами. Есть x - ϕ ( x , π ( 3 φ(Y,с)КYсномера, которые попадают во вторую категорию.Икс-φ(Икс,π(Икс3))

Так как есть номеров во всех категориях,Икс

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

и поэтому,

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

Числа в третьей категории имеют уникальное представление, если мы требуем, чтобы и, следовательно, p pq . Таким образом, произведение простых чиселpиqнаходится в третьей категории тогда и только тогда, когда 3 pxpq , так что естьπ(хx3<pqxpвозможных значений дляqдля фиксированного значенияpиP2(x,π(3√)π(xp)π(p)+1qp, гдеpkобозначаетkthп2(Икс,π(Икс3))знак равноΣπ(Икс3)<Кπ(Икс)(π(ИкспК)-π(пК)+1)пККго простое число.

Наконец, каждое положительное целое число , которое не взаимно простое с первыми c простыми числами, может быть уникальным образом выражено как n = p k f , где p k - наименьший простой множитель n . Таким образом, k c , и f взаимно прост с первыми числами k - 1 простых чисел.NYсNзнак равнопКепКNКсеК-1

Это приводит к рекурсивной формуле . В частности, сумма пуста, еслиc=0, поэтомуϕ(y,0)=y.φ(Y,с)знак равноY-Σ1Ксφ(YпК,К-1)сзнак равно0φ(Y,0)знак равноY

Теперь у нас есть формула, которая позволяет нам вычислить , генерируя только первое π ( 3 π(Икс)простые числа (миллионы против миллиардов).π(Икс23)

Алгоритм

Нам нужно вычислить , гдеpможет достигать3π(Иксп)п . Хотя есть и другие способы сделать это (например, применить нашу формулу рекурсивно), самый быстрый способ - перечислить все простые числа до3Икс3Икс23 , что можно сделать с помощью сита Эратосфена.

Сначала мы идентифицируем и храним все простые числа в и вычислимπ( 3 [1,Икс]иπ(π(Икс3)одновременно. Затем мы вычисляем хπ(Икс) для всехkв(π(3ИкспККи подсчитайте простые числа до каждого последующего отношения.(π(Икс3),π(Икс)]

Также имеет замкнутую формуπ( 3 Σπ(Икс3)<Кπ(Икс)(-π(пК)+1) , что позволяет нам завершить вычислениеP2(x,π(3π(Икс3)-π(Икс))(π(Икс3)+π(Икс)-12п2(Икс,π(Икс3)) .

Это оставляет вычисление , которое является самой дорогой частью алгоритма. Простое использование рекурсивной формулы потребует 2 вызова функции c для вычисления ϕ ( y , c )φ2сφ(Y,с) .

Прежде всего, для всех значений с , так что φ ( у , с ) = у - Σ 1 K C , P Kу φ ( уφ(0,с)знак равно0с. Само по себе этого наблюдения уже достаточно, чтобы сделать вычисление осуществимым. Это потому, что любое число ниже2Â109меньше, чем произведение любых десяти различных простых чисел, поэтому подавляющее большинство слагаемых исчезает.φ(Y,с)знак равноY-Σ1Кс,пКYφ(YпК,К-1)2109

Кроме того , с помощью группировки и первый Ĉ ' слагаемые из определения ф , мы получаем альтернативную формулу ф ( у , с ) = φ ( у , гр ' ) - Σ с ' < к с , р ку φ ( уYс'φ. Таким образом, предварительный расчетϕ(y,c)для фиксированногоcи соответствующих значенийyсохраняет большинство оставшихся вызовов функций и связанных с ними вычислений.φ(Y,с)знак равноφ(Y,с')-Σс'<Кс,пКYφ(YпК,К-1)φ(Y,с')с'Y

Если , то ϕ ( m c , c ) = φ ( m c ) , поскольку целые числа в [ 1 , m c ], которые не делятся ни на одно из p 1 , , p с как раз те, которые взаимно просты с т с . Кроме того, так как gcd ( z + m c ,мсзнак равноΠ1КспКφ(мс,с)знак равноφ(мс)[1,мс]п1,,псмс , имеем ϕ ( y , c ) = ϕ ( yНОД(Z+мс,мс)знак равноНОД(Z,мс)φ(Y,с)знак равноφ(Yмсмс,с)+φ(Y .

Поскольку функция Эйлера мультипликативна, , и у нас есть простой способ вывести ϕ ( y , c ) для всех y путем предварительного вычисления значений только для y в [ 0 , m c )φ(мс)знак равноΠ1Ксφ(пК)знак равноΠ1Кс(пК-1)φ(Y,с)YY[0,мс) .

Кроме того, если мы положим , мы получим ϕ ( y , c ) = ϕ ( y , c - 1 ) - ϕ ( yс'знак равнос-1, оригинальное определение из статьи Лемера. Это дает нам простой способ предварительно вычислитьϕ(y,c)для увеличения значенийc.φ(Y,с)знак равноφ(Y,с-1)-φ(Yпс,с-1)φ(Y,с)с

В дополнение к предварительному вычислению для некоторого, низкого значения c , мы также предварительно вычислим его для низких значений yφ(Y,с)сY , сокращая рекурсию после падения ниже определенного порогового значения.

Реализация

Предыдущий раздел охватывает большую часть кода. Еще одна важная деталь - как делятся функцииPhi выполняются .

Поскольку для вычисления требуется только деление на первое π ( 3 φпростые числа, мы можем использоватьфункцию вместо. Вместо простого деленияyна простоеp, мы умножаемyнаdp 2 64π(Икс3)fastdivYпY вместо и восстановитьуdп264п какдруYп . Из-за того, как вx64реализовано умножение целых чисел, деление на264не требуется; старшие 64 битаdpyхранятся в их собственном регистре.dпY264264dпY

Обратите внимание, что этот метод требует предварительного вычисления , которое не быстрее, чем вычисление ydп напрямую. Однако, поскольку мы должны делить на одни и те же простые числа снова и снова, а делениенамного медленнее,чем умножение, это приводит к значительному ускорению. Более подробную информацию об этом алгоритме, а также формальное доказательство можно найти вразделе Деление по инвариантным целым числам с использованием умножения.Yп


22
Кто-то не просто опережает Дениса?
Эддисон Крамп

8
Честно говоря, я просто не могу поверить, как быстро это. У меня не было времени, чтобы пройти и понять, что происходит, но мне действительно нужно.
Лиам

27
@Liam Я полностью собираюсь объяснить, как это работает, но я все еще пытаюсь ускорить это. Прямо сейчас, я действительно хочу, чтобы у PPCG был LaTeX ...
Деннис

15
Забавное примечание: (На моей машине) В настоящее время это превосходит встроенную в Mathematica и kimwalisch библиотеку github primecount C ++, однако в настоящее время это единственная запись, которая делает это.
Майкл Кляйн

10
@TheNumberOne тсс, не говори ему об этом ... другим людям может понадобиться это, чтобы победить его
Лиам

24

C99 / C ++, 8,9208 (28 февраля 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) );
    }

Реализация на основе растрового изображения на основе sras-of-erastothenes. Он выполняет следующие шаги:

  1. Во-первых, создайте повторяющийся битовый шаблон для заполнения сита, который охватывает кратные 2,3,5,7
  2. Затем используйте метод sieve, чтобы сгенерировать массив всех простых чисел, меньших, чем sqrt (n)
  3. Затем используйте список простых чисел из предыдущего шага, чтобы записать в сито. Это делается на кусках сита, размер которых приблизительно равен L1-кэшу, так что обработка сита не постоянно перебивает кэш L1; это, кажется, приводит к 5-кратному ускорению, а не к частоте.
  4. Наконец, выполните подсчет битов.

Скомпилированный gcc primecount.c -O3 -lm -Wallи работающий на ubuntu 15.10 (64-битная версия) на i7-4970k, он занимает около 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
Добро пожаловать в Programming Puzzles & Code Golf и поздравляем со звездным первым постом!
Деннис

Подумайте об использовании -O3 -march=native. Ваш ЦП поддерживает эту popcntинструкцию , и компиляторы иногда могут распознать некоторые ее чистые реализации на С и скомпилировать их в одну инструкцию. (Или лучше, используйте __builtin_popcountllна GNU C, как ответ Денниса).
Питер Кордес

-march=nativeна вашем процессоре Haswell также активирует BMI2 для более эффективных команд смещения с переменным числом. ( SHLX вместо устаревшего SHL, который нужно учитывать cl.) Процессор AMD Piledriver OP не имеет BMI2, но имеет popcnt. Но процессоры AMD работают с переменным числом SHL быстрее, чем процессоры Intel, поэтому компиляция с BMI2 во время настройки может быть уместной. Piledriver довольно сильно отличается от Haswell в том, что касается микрооптимизации, но просить -march=nativeэто хорошо
Питер Кордес

12

Python 2 (PyPy 4.0), 2.36961s (29 февраля 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])

Здесь используется метод Мейсселя-Лемера.

Задержки

$ 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
Эддисон Крамп

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% -ной скорости процессора. Это абсолютно эффективно, о чем ты говоришь?
Эддисон Крамп

Можете ли вы дать мне учебник по выходу о том, как Java (потому что C / C ++> Java). Я компилирую с javac voteToClose.java(я переименовал класс), и что потом?
Лиам

@Liamjava voteToClose <input>
Эддисон Крамп,

1
Подождите ... Почему байт-код говорит cafe babe?
Cyoce

12
@Cyoce Все файлы классов Java имеют заголовок 0xCAFEBABE.
Эддисон Крамп

8

Ржавчина, 0.37001 сек (12 июня 2016 г.)

Примерно в 10 раз медленнее, чем медленнее, чем Cответ Денниса , но в 10 раз быстрее, чем его запись на Python. Этот ответ стал возможен благодаря @Shepmaster и @Veedrac, которые помогли улучшить его в Code Review . Это взято дословно из поста @ 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
}

Приурочен с: 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,549 (11 ноября 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)

Наконец-то дошло время переделать это, и оно стало меньше / проще и НАМНОГО быстрее, чем раньше. Вместо более медленного метода грубой силы, он использует Сито Эратосфена наряду с более эффективными структурами данных, так что теперь он действительно может закончить за приемлемое время (насколько я могу найти в Интернете, это самый быстрый подсчет простых чисел 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. Я думаю, что есть единственная инструкция для увеличения значения, поэтому я не уверен.
Исмаэль Мигель

Почему это так сжато? Это не код-гольф , и это действительно трудно прочитать.
Cyoce

Кроме того, это может помочь изменить (...)|0;i=0на(...)|(i=0)
Cyoce

6

C ++ 11, 22,6503 (28 февраля 2016 г.)

Компилировать с g++ -O2 -m64 -march=native -ftree-vectorize -std=c++11 numprimes.cpp. Эти параметры важны. Вам также необходимо установить Boost . В Ubuntu это доступно при установке libboost-all-dev.

Если вы работаете в Windows, я могу порекомендовать установить g++и повысить через MSYS2 . Я написал хороший учебник о том, как установить 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;
}

На моей машине это работает за 4,8 секунды для 1907000000 (1,9e9).

Приведенный выше код был переназначен из моей личной библиотеки 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 Дайюм. Это быстро. Какая у тебя машина?
Эддисон Крамп

@VoteToClose Intel i5-4670k под управлением 64-разрядной Windows 7.
orlp

хотите добавить объяснение?
Лиам

@Liam Это просто сито с любым числом, кратным 2 и 3, оставленным вне сита.
orlp

3

C ++, 2.47215 (29 февраля 2016 г.)

Это (неаккуратная) многопоточная версия моего другого ответа.

#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

неофициальные сроки

Приуроченный с Intel i5-6600k на Ubuntu 15.10, дело 1907000000 взял 0.817s.

Официальные времена

Чтобы получить более точное время, я рассчитал это 100 раз, а затем разделил время на 100.

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

Так как этот ответ и ответ Python @Dennis очень близки, я могу изменить их время для получения более точных результатов.
Лиам

Вау вау вау. Это имеет для меня даже меньшее значение, чем CJam или Pyth. Я назову это монстром сдвига битов! +1
Тамогна Чоудхури

Кроме того, не могли бы вы попробовать CUDA / OpenCL для ускорения графического процессора? Если бы я знал больше C, я мог бы иметь.
Тамогна Чоудхури

Да, я думаю, что я был немного чрезмерен с битшифтингом / маскированием: PI не знает, будет ли здесь полезен GPGPU или нет; единственная область, которая, как я вижу, может помочь, - это предварительное просеивание небольших простых чисел, и даже тогда скорости передачи данных может быть достаточно, чтобы убить это. Что по-прежнему огорчает меня, так это то, что я все еще в 10 раз от самой быстрой реализации сит, которую я когда-либо видел
helloworld922

2

C, 2m42,7254s (28 февраля 2016 г.)

Сохранить как 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;
}

Требуется много памяти для запуска! Если ваше аппаратное обеспечение не может сэкономить до двух гигабайт реальной памяти, программа может либо аварийно завершить работу, либо работать очень медленно из-за перегрузки 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

Это работает с использованием сито из эратосфена? Я приму время, когда вернусь домой
Лиам

Я в первом случае segfaulting (другие работают нормально). Это происходит после ~ 1 минуты работы. Я добавил if (p==NULL) {exit(1);}строку в код, так что я не верю, что malloc не работает (также он потерпит неудачу в начале, а не через 1 минуту). Идеи о том, что происходит?
Лиам

Многие системы, в том числе Linux, делают оптимистичное распределение. Например, если вы попросите 1 Гб, он «отдаст» вам, но когда вы на самом деле перейдете к его использованию, и если система не сможет его найти, произойдет сбой. Если бы это было так, это, вероятно, было бы сбой в memset. Минута, которую он занимает, - это время, потраченное на объединение кучи в непрерывный блок. Также проверьте в вашей системе sizeof (bool) == 1. Если это == 4, то я могу переписать это, чтобы использовать char.

Я уже проверил. Bool составляет 1 байт. Можно ли просто запросить 2 * 10 ^ 9 байт памяти в стеке? Т.е. объявлять глобальную переменную, которая (на gcc), я полагаю, будет инициирована на 0. Это потребует использования charвместо этого, хотя я думаю.
Лиам

1
@ Лиам Трудно сказать. Целочисленное переполнение со знаком - неопределенное поведение, поэтому, не глядя на сгенерированную сборку, трудно предсказать, что сделал компилятор.
Деннис

2

Юлия, 1м

Я хотел бы придумать что-нибудь немного быстрее, но пока вот довольно наивная реализация Решета Эратосфена.

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
Я реализовал Сито Аткина, и моя реализация для этого медленнее. >: U
Алекс А.

@ Лиам Вау. Интересно, почему официальные времена намного длиннее моих неофициальных? Официальные времена довольно ужасны.
Алекс А.

Ну, официальное время для всех случаев вместе взятых. Неофициальные идут номер за номером. Кроме того, мой компьютер, вероятно, не так быстро, как ваш.
Лиам

@Liam О, это имеет больше смысла. Черт, я думал, что это было прилично. Ну что ж, вернемся к чертежной доске.
Алекс А.

Я собираюсь украсть алгоритм Денниса ... просто, чтобы понять, как быстро он работает.
Лиам

2

Java, 42.663122s * (3 марта 2016 г.)

* это было рассчитано программой изнутри (хотя на компьютере ОП)

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 по самодокументированию кода (хотя и не в буквальном смысле: р).

Это доказывает, что Java может быть достаточно быстрой, чтобы быть конкурентоспособной с другими языками ВМ при использовании аналогичных алгоритмов.

Запустить информацию

Запустите его так, как вы получите ответ @ CoolestVeto, но мой не нуждается в аргументах командной строки, он может получить их из STDIN.

Настройте NUM_THREADSконстанту, чтобы установить ее в 2 раза больше собственного ядра для максимальной производительности (как я заметил - в моем случае у меня есть 8 виртуальных ядер, поэтому для него установлено значение 16, для OP может потребоваться 12 для его шестиядерного процессора).

Когда я запускал эти тесты, я использовал JDK 1.7.0.45 с BlueJ 3.1.6 (IntelliJ обновлялся) на Windows 10 Enterpise x64 на ноутбуке ASUS K55VM (Core i7 3610QM, 8 ГБ ОЗУ). 64-разрядная версия Google Chrome 49.0 с открытой 1 вкладкой (PPCG) и загрузкой 1 файла QBittorrent выполнялась в фоновом режиме, при запуске 60% ОЗУ.

В основном,

javac PrimeCounter.java
java PrimeCounter

Программа проведет вас через все остальное.

Сроки сделаны встроенным Java System.nanoTime().

Детали алгоритма:

Имеет 3 варианта для разных вариантов использования - наивная версия, такая как @ CoolestVeto (но многопоточная) для входов ниже 2 ^ 15, и сито Эратосфена с битовой маской с нечетным исключением для входов выше 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) (без подсказок) может сэкономить некоторое время, потому что STDOUT ... ну, скажем так, он может быть немного быстрее.
user48538

@ zyabin101, если у кого-нибудь хватит терпения пройти код, он / она поймет, что учитывается задержка STDOUT.
Тамогна Чоудхури

Также из-за времени я посылаю стандартный вывод в / dev / null
Лиам

@ Лиам, я думаю, тебе придется сделать исключение в моем случае. Вы можете настроить основной метод для аргументов командной строки, но программа в любом случае работает автоматически. Проверьте это в любом случае. Пожалуйста?
Тамогна Чоудхури

Конечно я буду. Я сделаю это завтра. Если у меня возникнут проблемы, я буду пинговать тебя в чате
Лиам

2

Python 3

import sys

sys.setrecursionlimit(sys.maxsize)

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

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

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

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

print(sum(p) - 2)

Использует сито Эратосфена. Работает в среднем 8.775sгде n = 10^7. В свое время я использовал встроенную timeкоманду. Например:

$ time python3 test.py 90
24

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

Это сито! Я не мог использовать это в Java, потому что ему не нравилось, сколько памяти использует логический массив. D:
Эддисон Крамп

ошибка памяти в больших случаях.
Лиам

Какие случаи? Я считаю, что я это исправил. @Liam
Зак Гейтс

2
@VoteToClose Тогда не используйте логический массив. Используйте целочисленный массив и сдвиг / маскирование битов, где каждый бит представляет логическое значение.
mbomb007

AttributeError: 'module' object has no attribute 'maxint'
Деннис

1

C ++, 9.3221 (29 февраля 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';
}

Использует сегментированное сито Эратосфена с факторизацией колеса 6, чтобы пропустить все кратные 2/3. Использует POSIX ffsllдля пропуска последовательных составных значений.

Потенциально может быть ускорено, если сегментированное сито будет работать параллельно.

Скомпилировать:

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

неофициальные сроки

Приуроченный с Intel i5-6600k на Ubuntu 15.10, дело 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.