C, 0,026119s (12 Mar 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);
}
Ini menggunakan metode Meissel-Lehmer .
Pengaturan waktu
Di mesin saya, saya mendapatkan sekitar 5,7 milidetik untuk kasus uji gabungan. Ini menggunakan Intel Core i7-3770 dengan RAM DDR3 pada 1867 MHz, menjalankan 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
Karena varians menjadi terlalu tinggi , saya menggunakan timing dari dalam program untuk waktu berjalan tidak resmi. Ini adalah skrip yang menghitung rata-rata waktu menjalankan gabungan.
#!/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
Waktu resmi
Kali ini adalah untuk melakukan skor kasus 1000 kali.
real 0m28.006s
user 0m15.703s
sys 0m14.319s
Bagaimana itu bekerja
Rumus
Biarkan menjadi bilangan bulat positif.x
Setiap bilangan bulat positif memenuhi persis salah satu dari kondisi berikut.n ≤ x
n = 1
dapat dibagi dengan bilangan prima p dalam [ 1 , 3 √nhal.[ 1 , x--√3]
, di mana p dan q adalah (tidak harus berbeda) bilangan prima dalam ( 3 √n = p qhalq.( x--√3, x2--√3)
adalah prima dan n > 3 √nn > x--√3
Misalkan menunjukkan jumlah bilangan prima p sehingga p ≤ y . Ada π ( x ) - π ( 3 √π( y)halp ≤ yangka yang termasuk dalam kategori keempat.π( x ) - π( x--√3)
Misalkan menunjukkan jumlah bilangan bulat positif m ≤ y yang merupakan produk dari bilangan prima k yang tepat bukan di antara bilangan prima c pertama . Ada P 2 ( x , π ( 3 √Pk(y,c)m≤ykcangka yang termasuk dalam kategori ketiga.P2(x,π(x−−√3))
Akhirnya, misalkan menunjukkan jumlah bilangan bulat positif k ≤ y yang merupakan coprime ke bilangan prima c pertama . Ada x - ϕ ( x , π ( 3 √ϕ(y,c)k≤ycx−ϕ(x,π(x−−√3)) angka yang termasuk dalam kategori kedua.
Karena ada angka di semua kategori,x
1 + x - ϕ ( x , π( x--√3) ) + P2( x , π( x--√3) ) + π( x ) - π( x--√3) = x
dan maka dari itu,
π( x ) = ϕ ( x , π( x--√3) ) + π( x--√3) - 1 - P2( x , π( x--√3) )
Angka-angka dalam kategori ketiga memiliki representasi unik jika kita memerlukan dan, oleh karena itu p ≤ √p≤q . Dengan cara ini, produk bilangan primapdanqberada dalam kategori ketiga jika dan hanya jika 3 √p≤x−−√pq , jadi adaπ(xx−−√3<p≤q≤xpnilai yang mungkin untukquntuk nilai tetapp, danP2(x,π(3√π(xp)−π(p)+1qp, di manapkmenunjukkankthP2(x,π(x−−√3))=∑π(x√3)<k≤π(x√)(π(xpk)−π(pk)+1)pkkth bilangan prima.
Akhirnya, setiap bilangan bulat positif yang tidak coprime untuk pertama c bilangan prima dapat dinyatakan dalam cara yang unik sebagai n = p k f , di mana p k adalah faktor utama terendah n . Dengan cara ini, k ≤ c , dan f adalah koprime ke bilangan prima k - 1 pertama.n ≤ ycn = pkfhalknk ≤ cfk - 1
Ini mengarah ke rumus rekursif . Secara khusus, jumlah kosong jikac=0, jadiϕ(y,0)=y.ϕ ( y, c ) = y- ∑1 ≤ k ≤ cϕ ( yhalk, k - 1 )c = 0ϕ ( y, 0 ) = y
Kami sekarang memiliki formula yang memungkinkan kami untuk menghitung hanya dengan menghasilkan π ( 3 √ pertamaπ( x )bilangan prima (jutaan vs miliaran).π( x2--√3)
Algoritma
Kita harus menghitung , di manapbisa serendah3√π( xhal)p . Meskipun ada cara lain untuk melakukan ini (seperti menerapkan rumus kami secara rekursif), cara tercepat tampaknya adalah untuk menghitung semua bilangan prima hingga3 √x−−√3 , yang dapat dilakukan dengan ayakan Eratosthenes.x2−−√3
Pertama, kami mengidentifikasi dan menyimpan semua bilangan prima di , dan menghitungπ( 3 √[1,x−−√]danπ(√π(x−−√3)pada saat bersamaan. Kemudian, kita menghitung xπ(x−−√) untuk semuakdalam(π(3√xpkk, dan hitung bilangan prima hingga masing-masing hasil bagi.(π(x−−√3),π(x−−√)]
Juga, memiliki bentuk tertutupπ( 3 √∑π(x√3)<k≤π(x√)(−π(pk)+1) , yang memungkinkan kita untuk menyelesaikan perhitunganP2(x,π(3√π(x√3)−π(x√))(π(x√3)+π(x√)−12P2(x,π(x−−√3)) .
Itu meninggalkan perhitungan , yang merupakan bagian paling mahal dari algoritma. Cukup menggunakan rumus rekursif akan membutuhkan panggilan fungsi 2 c untuk menghitung ϕ ( y , c )ϕ2cϕ(y,c) .
Pertama-tama, untuk semua nilai c , jadi ϕ ( y , c ) = y - ∑ 1ϕ(0,c)=0c . Dengan sendirinya, pengamatan ini sudah cukup untuk membuat perhitungan layak. Ini karena angka di bawah 2 ⋅ 10 9ϕ(y,c)=y−∑1≤k≤c,pk≤yϕ(ypk,k−1)2⋅109 lebih kecil dari produk dari sepuluh bilangan prima yang berbeda, sehingga sebagian besar dari jumlah puncak menghilang.
Juga, dengan mengelompokkan dan yang pertama c ' summands dari definisi φ , kita mendapatkan rumus alternatif φ ( y , c ) = φ ( y , c ' ) - Σ c ' < k ≤ c , p k ≤ y φ ( yyc′ϕ. Dengan demikian, precomputingφ(y,c')untuk tetapc'dan nilai-nilai yang sesuai dariymenyimpan sebagian besar fungsi panggilan tersisa dan perhitungan terkait.ϕ ( y, c ) = ϕ ( y, c′) - ∑c′< k ≤ c , hlmk≤ yϕ ( yhalk, k - 1 )ϕ ( y, c′)c′y
Jika , maka ϕ ( m c , c ) = φ ( m c ) , karena bilangan bulat dalam [ 1 , m c ] yang dapat dibagi dengan tidak ada p 1 , ⋯ , p cmc= ∏1 ≤ k ≤ chalkϕ ( mc, c ) = φ ( mc)[ 1 , mc]hal1, ⋯ , hlmc tepatnya mereka yang koprime ke . Juga, karena gcd ( z + m c , mmc , kita memiliki ϕ ( y , c ) = ϕ ( ⌊ ygcd ( z+ mc, mc) = gcd ( z, mc).ϕ ( y, c ) = ϕ ( ⌊ ymc⌋ mc, c ) + ϕ ( y
Karena fungsi total Euler adalah multiplikatif, , dan kami memiliki cara mudah untuk memperoleh ϕ ( y , c ) untuk semua y dengan mengkomputasi nilai hanya untuk y di [ 0 , m c )φ(mc)=∏1≤k≤cφ(pk)=∏1≤k≤c(pk−1)ϕ(y,c)yy[0,mc) .
Juga, jika kita menetapkan , kita memperoleh ϕ ( y , c ) = ϕc′=c−1ϕ(y,c)=ϕ(y,c−1)−ϕ(ypc,c−1) , definisi asli dari makalah Lehmer. Ini memberi kita cara sederhana untuk melakukan precompute untuk meningkatkan nilai cϕ(y,c)c .
Selain untuk precomputing untuk nilai c tertentu yang rendah, kami juga akan melakukan precompute untuk nilai y yang rendahϕ(y,c)cy , memotong rekursi sesaat setelah jatuh di bawah ambang batas tertentu.
Penerapan
Bagian sebelumnya mencakup sebagian besar kode. Satu detail penting yang tersisa adalah bagaimana pembagian fungsiPhi
dilakukan.
Karena komputasi hanya membutuhkan pembagian dengan π pertama ( 3 √ϕbilangan prima, kita bisa menggunakanfungsi sebagai gantinya. Daripada hanya membagiydenganpprima, kita mengalikanyπ(x−−√3)fastdiv
ypy dengan sebagai gantinya dan pulihkanydp≈264pyp as . Karena cara multiplikasi integer diimplementasikan padax64, bagi dengan2dpy264 tidak diperlukan; lebih tinggi 64 bit d p ydisimpan dalam register mereka sendiri.264dpy
Perhatikan bahwa metode ini membutuhkan precomputing , yang tidak lebih cepat dari komputasi ydpyp secara langsung. Namun, karena kita harus membaginya dengan bilangan prima yang sama berulang-ulang dan pembagian jauh lebih lambat daripada perkalian, ini menghasilkan percepatan yang penting. Rincian lebih lanjut tentang algoritma ini, serta bukti formal, dapat ditemukan di Division oleh Invariant Integers menggunakan Multiplication .