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毫秒。这是在Intel Core i7-3770和DDR3 RAM在1867 MHz上运行的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
每个正整数完全满足以下条件之一。ñ ≤ X
n = 1
p [ 1 ,3 √ñ整除由素数在。p[ 1 ,X--√3]
p q (3 √n = p q,其中和是(中的(不一定是互斥的)素数。pq(x--√3,X2--√3)
Ñ > 3 √ñ是素数,n > x--√3
令表示素数使得。有数字属于第四类。p p ≤ ÿ π (X )- π (3 √π(y)pp ≤ ÿπ(X )- π(x--√3)
Pk(y,c)m≤ykcP2(x,π(x−−√3))
ϕ(y,c)k≤ycx−ϕ(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≤qp≤x−−√pqx−−√3<p≤q≤xpπ(xp)−π(p)+1qpP2(x,π(x−−√3))=∑π(x√3)<k≤π(x√)(π(xpk)−π(pk)+1)pkkth
n≤ycn=pkfpknk≤cfk−1
ϕ(y,c)=y−∑1≤k≤cϕ(ypk,k−1)c=0ϕ(y,0)=y
π(x)π(x2−−√3)
算法
π(xp)px−−√3x2−−√3
[1,x−−√]π(x−−√3)π(x−−√)xpkk(π(x−−√3),π(x−−√)]
∑π(x√3)<k≤π(x√)(−π(pk)+1)π(x√3)−π(x√))(π(x√3)+π(x√)−12P2(x,π(x−−√3))
ϕ2cϕ(y,c)
ϕ(0,c)=0cϕ(y,c)=y−∑1≤k≤c,pk≤yϕ(ypk,k−1)2⋅109
yc′ϕϕ(y,c)=ϕ(y,c′)−∑c′<k≤c,pk≤yϕ(ypk,k−1)ϕ(y,c′)c′y
mc=∏1≤k≤cpkϕ(mc,c)=φ(mc)[1,mc]p1,⋯,pcmcgcd(z+mc,mc)=gcd(z,mc)ϕ(y,c)=ϕ(⌊ymc⌋mc,c)+ϕ(y。
由于Euler的totient函数是可乘的,因此,我们有一种简单的方法来导出的所有通过用于仅那些预先计算的值在。φ(mc)=∏1≤k≤cφ(pk)=∏1≤k≤c(pk−1)ϕ(y,c)yy[0,mc)
另外,如果我们设置,我们将获得, Lehmer论文的原始定义。这为我们提供了一种简单的方法来预先计算来增加值。c′=c−1ϕ(y,c)=ϕ(y,c−1)−ϕ(ypc,c−1)ϕ(y,c)c
除了针对某个较低的值预先计算,我们还将针对较低的值对其进行预先计算,从而在低于某个阈值之后缩短递归。ϕ(y,c)cy
实作
上一节涵盖了代码的大部分。剩下的一个重要细节是如何Phi
执行功能划分。
由于计算只需要除以第一个质数,我们可以改用函数。而不是简单地将一个由原,我们乘通过代替和恢复作为。由于在x64上实现整数乘法的方式,因此不需要除以。的高64位存储在它们自己的寄存器中。ϕπ(x−−√3)fastdiv
ypydp≈264pyp 264dpÿdpy264264dpy
请注意,此方法需要预先计算,这并不比直接计算要快。但是,由于我们必须一遍又一遍地除以相同的素数,并且除法要比乘法慢得多,因此这导致了重要的加速。关于此算法的更多细节以及形式证明,可以在使用乘法的不变整数除法中找到。ÿdpyp