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);
}
นี้ใช้วิธี 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
ครั้งอย่างเป็นทางการ
เวลานี้สำหรับทำคะแนนกรณี 1,000 ครั้ง
real 0m28.006s
user 0m15.703s
sys 0m14.319s
มันทำงานอย่างไร
สูตร
ให้เป็นจำนวนเต็มบวกx
จำนวนเต็มบวกแต่ละตัวตรงตามเงื่อนไขข้อใดข้อหนึ่งดังต่อไปนี้n≤x
n=1
nคือหารด้วยจำนวนเฉพาะใน{x}]p[1,x−−√3]
n=pqที่และมี (ไม่จำเป็นต้องแตกต่างกัน) ตัวเลขที่สำคัญใน2})pq(x−−√3,x2−−√3)
nเป็นจำนวนเฉพาะและn>x−−√3
Letแสดงว่าจำนวนของจำนวนเฉพาะดังกล่าวว่าY มีตัวเลขที่อยู่ในหมวดหมู่ที่สี่π(y)pp≤yπ(x)−π(x−−√3)
ให้หมายถึงปริมาณของจำนวนเต็มบวกที่มีสินค้าของตรงตัวเลขที่สำคัญไม่ได้ในหมู่คนแรกที่ตัวเลขที่สำคัญ มีตัวเลขที่อยู่ในหมวดที่สามPk(y,c)m≤ykcP2(x,π(x−−√3))
สุดท้ายให้หมายถึงปริมาณของจำนวนเต็มบวกที่มี coprime ไปก่อนตัวเลขที่สำคัญ มีหมายเลขที่อยู่ในหมวดที่สองϕ(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))
ตัวเลขในประเภทที่สามมีการแสดงที่ไม่ซ้ำกันถ้าเราจำเป็นต้องให้และดังนั้น{x} วิธีนี้ผลิตภัณฑ์ของ primesและอยู่ในหมวดที่สามหากดังนั้นจึงมีค่าที่เป็นไปได้สำหรับสำหรับค่าคงที่ของและโดยที่หมายถึงจำนวนเฉพาะp≤qp≤x−−√pqx−−√3<p≤q≤xpπ(xp)−π(p)+1qpP2(x,π(x−−√3))=∑π(x√3)<k≤π(x√)(π(xpk)−π(pk)+1)pkkth
สุดท้ายทุกจำนวนเต็มบวกที่ไม่ coprime ไปก่อนตัวเลขที่สำคัญสามารถแสดงออกในแฟชั่นที่ไม่ซ้ำกันเป็นที่เป็นปัจจัยสำคัญต่ำสุดของnด้วยวิธีนี้และคือ coprime สำหรับจำนวนเฉพาะช่วงแรกn≤ycn=pkfpknk≤cfk−1
นี้นำไปสู่สูตร recursive1) โดยเฉพาะอย่างยิ่งรวมเป็นที่ว่างเปล่าถ้าดังนั้นyϕ(y,c)=y−∑1≤k≤cϕ(ypk,k−1)c=0ϕ(y,0)=y
ตอนนี้เรามีสูตรที่ช่วยให้เราสามารถคำนวณโดยสร้างเฉพาะหมายเลขแรก (ล้านเทียบกับพันล้านครั้ง)π(x)π(x2−−√3)
ขั้นตอนวิธี
เราจะต้องคำนวณที่จะได้รับที่ต่ำเป็น{x} ในขณะที่มีวิธีอื่นที่จะทำเช่นนี้ (เช่นการใช้สูตรซ้ำ) วิธีที่เร็วที่สุดที่จะระบุจำนวนเฉพาะทั้งหมดถึงซึ่งสามารถทำได้ด้วยตะแกรงของ Eratosthenesπ(xp)px−−√3x2−−√3
อันดับแรกเราระบุและเก็บหมายเลขเฉพาะทั้งหมดในและคำนวณและในเวลาเดียวกัน จากนั้นเราคำนวณสำหรับทุกในและนับจำนวนขึ้นอยู่กับแต่ละผลหารต่อเนื่อง .[1,x−−√]π(x−−√3)π(x−−√)xpkk(π(x−−√3),π(x−−√)]
นอกจากนี้มีรูปแบบปิดซึ่ง ช่วยให้เราสามารถเสร็จสิ้นการคำนวณของ{x}))∑π(x√3)<k≤π(x√)(−π(pk)+1)π(x√3)−π(x√))(π(x√3)+π(x√)−12P2(x,π(x−−√3))
นั่นทำให้การคำนวณซึ่งเป็นส่วนที่แพงที่สุดของอัลกอริทึม เพียงแค่ใช้สูตร recursive จะต้องมีเรียกฟังก์ชันในการคำนวณc)ϕ2cϕ(y,c)
ก่อนอื่นสำหรับค่าทั้งหมดของดังนั้น1) การสังเกตนี้มีอยู่แล้วเพียงพอที่จะทำให้การคำนวณเป็นไปได้ นี่เป็นเพราะตัวเลขใด ๆ ที่ต่ำกว่ามีขนาดเล็กกว่าผลิตภัณฑ์ของสิบช่วงเวลาที่แตกต่างกันϕ(0,c)=0cϕ(y,c)=y−∑1≤k≤c,pk≤yϕ(ypk,k−1)2⋅109
ยิ่งไปกว่านั้นด้วยการจัดกลุ่มและสรุปคำนิยามของเราจะได้สูตรทางเลือก1) ดังนั้นการคำนวณล่วงหน้าสำหรับค่าคงที่และค่าที่เหมาะสมของจะบันทึกการเรียกใช้ฟังก์ชันที่เหลืออยู่ส่วนใหญ่และการคำนวณที่เกี่ยวข้องyc′ϕϕ(y,c)=ϕ(y,c′)−∑c′<k≤c,pk≤yϕ(ypk,k−1)ϕ(y,c′)c′y
ถ้าดังนั้นเนื่องจากจำนวนเต็มในที่ไม่มีการหารด้วยได้อย่างแม่นยำผู้ที่มี coprime เพื่อm_cนอกจากนี้เนื่องจากเรามีค)mc=∏1≤k≤cpkϕ(mc,c)=φ(mc)[1,mc]p1,⋯,pcmcgcd(z+mc,mc)=gcd(z,mc)ϕ(y,c)=ϕ(⌊ymc⌋mc,c)+ϕ(y
เนื่องจากฟังก์ชัน totient ของออยเลอร์คือ multiplicative,และเรามี เป็นวิธีที่ง่ายที่จะได้รับทุกโดย precomputing ค่าสำหรับเฉพาะผู้ในm_c)φ(mc)=∏1≤k≤cφ(pk)=∏1≤k≤c(pk−1)ϕ(y,c)yy[0,mc)
นอกจากนี้ถ้าเราตั้งเราได้รับการ คำนิยามดั้งเดิมจากกระดาษของ Lehmer นี้ทำให้เรามีวิธีง่ายๆในการ precomputeสำหรับการเพิ่มค่าของคc′=c−1ϕ(y,c)=ϕ(y,c−1)−ϕ(ypc,c−1)ϕ(y,c)c
นอกจากนี้สำหรับการคำนวณล่วงหน้าสำหรับค่าแน่นอนและต่ำเราจะคำนวณล่วงหน้าสำหรับค่าที่ต่ำของด้วยการตัดการเรียกซ้ำสั้น ๆ หลังจากลดลงต่ำกว่าเกณฑ์ที่กำหนดϕ(y,c)cy
การดำเนินงาน
ส่วนก่อนหน้าครอบคลุมส่วนใหญ่ของรหัส หนึ่งที่เหลือรายละเอียดที่สำคัญคือวิธีการที่หน่วยงานในฟังก์ชั่นที่Phi
จะดำเนินการ
เนื่องจากการคำนวณต้องการเพียงการหารด้วยจำนวนเฉพาะเราจึงสามารถใช้ฟังก์ชันแทนได้ มากกว่าแค่การหารด้วยไพร์มเราคูณด้วยแทนและกู้เป็น{64}} เนื่องจากวิธีการคูณจำนวนเต็มถูกนำไปใช้ในx64จึงไม่จำเป็นต้องหารด้วย64 บิตที่สูงกว่าของจะถูกเก็บไว้ในการลงทะเบียนของตัวเองϕπ(x−−√3)fastdiv
ypydp≈264pyp 264dpydpy264264dpy
โปรดทราบว่าวิธีนี้ต้องใช้การคำนวณล่วงหน้าซึ่งไม่เร็วกว่าการคำนวณโดยตรง อย่างไรก็ตามเนื่องจากเราต้องหารด้วยจำนวนเฉพาะซ้ำแล้วซ้ำอีกและการหารนั้นช้ากว่าการคูณมากดังนั้นผลลัพธ์นี้จึงเป็นความเร็วที่สำคัญ รายละเอียดเพิ่มเติมเกี่ยวกับขั้นตอนวิธีนี้เช่นเดียวกับหลักฐานอย่างเป็นทางการสามารถพบได้ในการหารด้วยจำนวนเต็มคงใช้คูณYdpyp