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
Как это устроено
формула
Пусть x - положительное целое число.
Каждое натуральное число удовлетворяет ровно одному из следующих условий.n≤x
n=1
делится на простое число p в [ 1 , 3 √np.[1,x−−√3]
, где p и q - (не обязательно различающиеся) простые числа в ( 3 √n=pqpq.(x−−√3,x2−−√3)
простое и n > 3 √nn>x−−√3
Пусть обозначает число простых чисел p, таких что p ≤ y . Есть π ( x ) - π ( 3 √π(y)pp≤yчисла, которые попадают в четвертую категорию.π(x)−π(x−−√3)
Пусть обозначает количество натуральных чисел m ≤ y, которые являются произведением ровно k простых чисел, не входящих в число первых c простых чисел. Есть P 2 ( x , π ( 3 √Pk(y,c)m≤ykcномера, которые попадают в третью категорию.P2(x,π(x−−√3))
Наконец, пусть обозначает количество натуральных чисел k ≤ y , которые взаимно просты с первыми c простыми числами. Есть x - ϕ ( x , π ( 3 √ϕ(y,c)k≤ycномера, которые попадают во вторую категорию.x−ϕ(x,π(x−−√3))
Так как есть номеров во всех категориях,x
1+x−ϕ(x,π(x−−√3))+P2(x,π(x−−√3))+π(x)−π(x−−√3)=x
и поэтому,
π(x)=ϕ(x,π(x−−√3))+π(x−−√3)−1−P2(x,π(x−−√3))
Числа в третьей категории имеют уникальное представление, если мы требуем, чтобы и, следовательно, p ≤ √p≤q . Таким образом, произведение простых чиселpиqнаходится в третьей категории тогда и только тогда, когда 3 √p≤x−−√pq , так что естьπ(хx−−√3<p≤q≤xpвозможных значений дляqдля фиксированного значенияpиP2(x,π(3√)π(xp)−π(p)+1qp, гдеpkобозначаетkthп2( х , я( х--√3) ) = ∑π( х√3) < k ≤ π( х√)( π( хпК) - π( рК) + 1 )пККго простое число.
Наконец, каждое положительное целое число , которое не взаимно простое с первыми c простыми числами, может быть уникальным образом выражено как n = p k f , где p k - наименьший простой множитель n . Таким образом, k ≤ c , и f взаимно прост с первыми числами k - 1 простых чисел.n ≤ yсn = pКепКNk ≤ cек - 1
Это приводит к рекурсивной формуле . В частности, сумма пуста, еслиc=0, поэтомуϕ(y,0)=y.ϕ ( у, С ) = у- ∑1 ≤ k ≤ cϕ ( упК, к - 1 )с = 0ϕ ( у, 0 ) = у
Теперь у нас есть формула, которая позволяет нам вычислить , генерируя только первое π ( 3 √π( х )простые числа (миллионы против миллиардов).π( х2--√3)
Алгоритм
Нам нужно вычислить , гдеpможет достигать3√π( хп)п . Хотя есть и другие способы сделать это (например, применить нашу формулу рекурсивно), самый быстрый способ - перечислить все простые числа до3 √Икс--√3Икс2--√3 , что можно сделать с помощью сита Эратосфена.
Сначала мы идентифицируем и храним все простые числа в и вычислимπ( 3 √[ 1 , х--√]иπ(√π( х--√3)одновременно. Затем мы вычисляем хπ( х--√) для всехkв(π(3√ИкспККи подсчитайте простые числа до каждого последующего отношения.( π( х--√3) , π( х--√) ]
Также имеет замкнутую формуπ( 3 √Σπ( х√3) < k ≤ π( х√)( - π( рК) + 1 ) , что позволяет нам завершить вычислениеP2(x,π(3√π( х√3) - π( х√) ) ( π( х√3) + π( х√) - 12п2( х , я( х--√3) ) .
Это оставляет вычисление , которое является самой дорогой частью алгоритма. Простое использование рекурсивной формулы потребует 2 вызова функции c для вычисления ϕ ( y , c )φ2сϕ ( у, С ) .
Прежде всего, для всех значений с , так что φ ( у , с ) = у - Σ 1 ≤ K ≤ C , P K ≤ у φ ( уϕ ( 0 , c ) = 0с. Само по себе этого наблюдения уже достаточно, чтобы сделать вычисление осуществимым. Это потому, что любое число ниже2Â109меньше, чем произведение любых десяти различных простых чисел, поэтому подавляющее большинство слагаемых исчезает.ϕ ( у, С ) = у- ∑1 ≤ k ≤ c , pК≤ уϕ ( упК, к - 1 )2 ⋅ 109
Кроме того , с помощью группировки и первый Ĉ ' слагаемые из определения ф , мы получаем альтернативную формулу ф ( у , с ) = φ ( у , гр ' ) - Σ с ' < к ≤ с , р к ≤ у φ ( уYс'φ. Таким образом, предварительный расчетϕ(y,c′)для фиксированногоc′и соответствующих значенийyсохраняет большинство оставшихся вызовов функций и связанных с ними вычислений.ϕ ( у, с ) = ϕ ( у, с') - ∑с'< k ≤ c , pК≤ уϕ ( упК, к - 1 )ϕ ( у, с')с'Y
Если , то ϕ ( m c , c ) = φ ( m c ) , поскольку целые числа в [ 1 , m c ], которые не делятся ни на одно из p 1 , ⋯ , p с как раз те, которые взаимно просты с т с . Кроме того, так как gcd ( z + m c ,мс= ∏1 ≤ k ≤ cпКϕ ( мс, С ) = φ ( мс)[ 1 , мс]п1, ⋯ , рсмс , имеем ϕ ( y , c ) = ϕ ( ⌊ ygcd ( z+ мс, мс) = gcd ( z, мс)ϕ ( у, c ) = ϕ ( ⌊ yмс⌋ мс, в ) + ϕ ( у .
Поскольку функция Эйлера мультипликативна, , и у нас есть простой способ вывести ϕ ( y , c ) для всех y путем предварительного вычисления значений только для y в [ 0 , m c )φ ( мс) = ∏1 ≤ k ≤ cφ ( рК) = ∏1 ≤ k ≤ c( рК- 1 )ϕ ( у, С )YY[ 0 , мс) .
Кроме того, если мы положим , мы получим ϕ ( y , c ) = ϕ ( y , c - 1 ) - ϕ ( yс'= с - 1, оригинальное определение из статьи Лемера. Это дает нам простой способ предварительно вычислитьϕ(y,c)для увеличения значенийc.ϕ ( у, с ) = ϕ ( у, c - 1 ) - ϕ ( yпс, С - 1 )ϕ ( у, С )с
В дополнение к предварительному вычислению для некоторого, низкого значения c , мы также предварительно вычислим его для низких значений yϕ ( у, С )сY , сокращая рекурсию после падения ниже определенного порогового значения.
Реализация
Предыдущий раздел охватывает большую часть кода. Еще одна важная деталь - как делятся функцииPhi
выполняются .
Поскольку для вычисления требуется только деление на первое π ( 3 √φпростые числа, мы можем использоватьфункцию вместо. Вместо простого деленияyна простоеp, мы умножаемyнаdp≈ 2 64π( х--√3)fastdiv
YпY вместо и восстановитьуdп≈ 264п какдруYп . Из-за того, как вx64реализовано умножение целых чисел, деление на264не требуется; старшие 64 битаdpyхранятся в их собственном регистре.dпY264264dпY
Обратите внимание, что этот метод требует предварительного вычисления , которое не быстрее, чем вычисление ydп напрямую. Однако, поскольку мы должны делить на одни и те же простые числа снова и снова, а делениенамного медленнее,чем умножение, это приводит к значительному ускорению. Более подробную информацию об этом алгоритме, а также формальное доказательство можно найти вразделе Деление по инвариантным целым числам с использованием умножения.Yп