সি, 0.026119 এস (মার্চ 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 মিলিসেকেন্ড পাচ্ছি । এটি 1867 মেগাহার্জ-এ ডিডিআর 3 র্যাম সহ একটি ইন্টেল কোর আই 7-3770, ওপেনসুএস 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
একটি মৌলিক সংখ্যা দিয়ে বিভাজ্য পি মধ্যে [ 1 , 3 √np।[1,x−−√3]
, যেখানে পি এবং কিউ ( 3 আলাদা √ মধ্যে মূল সংখ্যা পৃথক নয়) √n=pqpq।(x−−√3,x2−−√3)
মূল এবং n > 3 √ √nn>x−−√3
আসুন প্রাইম সংখ্যা পি যেমন p ≤ y নির্দেশ করে । এখানে π ( x ) - π ( 3 √ ) রয়েছে √π(y)pp≤yসংখ্যাগুলি যা চতুর্থ বিভাগে আসে।π(x)−π(x−−√3)
যাক ধনাত্মক পূর্ণসংখ্যা পরিমাণ বোঝাতে মি ≤ Y যে ঠিক এর একটি পণ্য হয় k প্রথম মধ্যে না মৌলিক সংখ্যার গ মৌলিক সংখ্যার। আছে পি 2 ( x এর , π ( 3 √Pk(y,c)m≤ykcসংখ্যাগুলি যা তৃতীয় বিভাগে আসে।P2(x,π(x−−√3))
অবশেষে, যাক ধনাত্মক পূর্ণসংখ্যা পরিমাণ বোঝাতে ট ≤ Y যে প্রথম থেকে coprime হয় গ মৌলিক সংখ্যার। এখানে x - ϕ ( x , π ( 3 √) রয়েছে √φ(y, গ )k ≤ yগসংখ্যা যা দ্বিতীয় বিভাগে আসে।x - ϕ ( x , π)( এক্স--√3) )
যেহেতু সমস্ত বিভাগে সংখ্যা রয়েছে,এক্স
1 + এক্স - ϕ ( এক্স , π)( এক্স--√3) ) + পি2(x,π(x−−√3))+π(x)−π(x−−√3)=x
এবং সেইজন্য,
π(x)=ϕ(x,π(x−−√3))+π(x−−√3)−1−P2(x,π(x−−√3))
যদি আমরা যে প্রয়োজন তৃতীয় শ্রেণীতে সংখ্যার একটি অনন্য উপস্থাপনা আছে , সেইজন্য পি ≤ √p ≤ q । এইভাবে, প্রাইমগুলিরpএবংq এরপণ্যটিতৃতীয় বিভাগে এবং যদি কেবল 3 3 হয় √p ≤ x−-√পিকুই , সুতরাংπ(এক্স) আছেএক্স--√3< পি ≤q। Xপিসম্ভাব্য মানকুইএর একটি নির্দিষ্ট মানের জন্যপি, আরপি2(x এর,π(3√π( এক্সপি) -π( পি ) + 1কুইপি, যেখানেপিটউল্লেখ করেটমমৌলিক সংখ্যা।পি2( এক্স ,π( এক্স--√3) ) = ∑π( এক্স√3) < ট ≤π( এক্স√)(π( এক্সপিট) -π( পিট) + 1 )পিটটম
অবশেষে, প্রতিটি ধনাত্মক পূর্ণসংখ্যা যা প্রথম সি প্রাথমিক সংখ্যার সাথে স্বীকৃতিপ্রাপ্ত নয় , অনন্য ফ্যাশনে এন = পি কে এফ হিসাবে প্রকাশ করা যেতে পারে , যেখানে পি কে এন এর সর্বনিম্ন মূল উপাদান । এইভাবে, কে ≤ সি , এবং এফ প্রথম ক - 1 টি প্রাথমিক সংখ্যাতে কপিরাইট হয়।n ≤ yগn = পিটচপিটএনk ≤ cচকে - 1
এর ফলে পুনরাবৃত্ত সূত্র । বিশেষত, যোগফল ফাঁকা যদিc=0 হয়, তাইϕ(y,0)=y।ϕ ( y), গ ) = y- ∑1 ≤ কে ≤ সিϕ ( y)পিট, কে - 1 )সি = 0ϕ ( y),0)=y
আমাদের কাছে এখন এমন একটি সূত্র রয়েছে যা কেবলমাত্র প্রথম π ( 3 √ ) জেনারেট করে আমাদের গণনা করতে দেয় √π(x)সংখ্যা (মিলিয়ন বনাম বিলিয়ন)।π(x2−−√3)
অ্যালগরিদম
আমাদের গুনতে হবে , যেখানেপি3as হিসাবে কম পেতে পারে√π(xp)p । যদিও সেখানে (যাও recursively আমাদের সূত্র প্রয়োগের মত) এই কাজ করতে অন্য কোন উপায়ে হয়, দ্রুততম উপায় সব primes গনা করতে হবে বলে মনে হয়3 √x−−√3 , যা ইরোটোস্টিনিসের চালনী দিয়ে করা যেতে পারে।x2−−√3
প্রথমত, আমরা [ 1 , in এ সমস্ত মৌলিক সংখ্যা চিহ্নিত করি এবং সঞ্চয় করি √, এবং গণনাπ( 3 √) √[1,x−−√]এবংπ(√)π( এক্স--√3)একই সাথে। তারপরে, আমরা x গণনা করবπ( এক্স--√) সবার জন্যটমধ্যে(π(3√এক্সপিটট, এবং প্রতিটি ক্রমান্বয়ে ভাগফলকে প্রাইমস গণনা করুন।( π)( এক্স--√3) , π( এক্স--√) ]
এছাড়াও, এর বন্ধ ফর্মπ( 3 √ )রয়েছে √Σπ( এক্স√3) < কে ≤ π( এক্স√)( - π)( পিট) + 1 ) , যা আমাদেরপি2(x,π(3√) এরগণনা সম্পূর্ণ করতে দেয়√π( এক্স√3) - π( এক্স√) ) ( π( এক্স√3) + + Π( এক্স√) - ঘ2।পি2( এক্স , π)( এক্স--√3) )
এটি এর গণনা ছেড়ে যায় যা আলগোরিদিমের সবচেয়ে ব্যয়বহুল অংশ। কেবলমাত্র রিকার্সিভ সূত্র ব্যবহার করে করতে হবে 2 গ ফাংশন গনা আহ্বান φ ( Y , গ ) ।φ2গϕ ( y), গ )
প্রথম সব, সব মানের জন্য গ , তাই φ ( Y , গ ) = Y - Σ 1 ≤ ট ≤ গ , পি ট ≤ Y φ ( Yϕ ( 0 , সি ) = 0গ। নিজেই, এই পর্যালোচনাটি ইতিমধ্যে গণনা সম্ভাব্য করে তোলার জন্য যথেষ্ট। এর কারণ হল নীচের যেকোনো সংখ্যা2⋅109কোন দশ স্বতন্ত্র মৌলিক সংখ্যার পণ্যের চেয়ে ছোট, তাই summands অপ্রতিরোধ্য সংখ্যাগুরু চলে যায়।ϕ ( y), গ ) = y- ∑1 ≤ কে ≤ সি , পিট≤ yϕ ( y)পিট, কে - 1 )2 ⋅ 109
এছাড়াও, গোষ্ঠীবদ্ধ দ্বারা এবং প্রথম গ ' সংজ্ঞার summands φ , আমরা বিকল্প সূত্র পেতে φ ( Y , গ ) = φ ( Y , গ ' ) - Σ গ ' < ট ≤ গ , পি ট ≤ Y φ ( y)Yগ'φ। সুতরাং, precomputingφ(Y,গ')একটি নির্দিষ্ট জন্যগ'এবং যথাযথ মানYফাংশন কল ও সংশ্লিষ্ট কম্পিউটেশন অবশিষ্ট অধিকাংশ পরিমাণ সঞ্চয় হয়।ϕ ( y), গ ) = ϕ ( y ), গ') - ∑গ'< কে ≤ সি , পিট≤ yϕ ( y)পিট, কে - 1 )ϕ ( y), গ')গ'Y
যদি , তারপর φ ( মি গ , গ ) = φ ( মি গ ) , ইন পূর্ণসংখ্যার থেকে [ 1 , মি গ ] যে কেউ দ্বারা বিভাজ্য হয় পি 1 , ⋯ , পি গ অবিকল সেই যে coprime হয় মি গ । এছাড়াও, যেহেতু গিসিডি ( জেড + এম সি , এম)মিগ= ∏1 ≤ কে ≤ সিপিটϕ(mc,c)=φ(mc)[1,mc]p1,⋯,pcmc , আমাদের কাছে এটি ϕ ( y , c ) = ϕ ( ⌊ y)gcd(z+mc,mc)=gcd(z,mc)।ϕ(y, গ )=ϕ(⌊ymc⌋mc, গ ) + + φ(y
যেহেতু ইউলার totient ফাংশন গুণনশীল হয়, , এবং আমরা আহরণ একটি সহজ উপায় আছে φ ( Y , গ ) সবার জন্য Y কেবলমাত্র সেই মান precomputing দ্বারা Y মধ্যে [ 0 , মি গ ) ।φ ( মি।)গ) = ∏1 ≤ কে ≤ সিφ ( পিট) = ∏1 ≤ কে ≤ সি( পিট- 1 )ϕ (y, গ )YY[ 0 , মিগ)
এছাড়াও, যদি আমরা সেট , আমরা প্রাপ্ত φ ( Y , গ ) = φ ( Y , গ - 1 ) - φ ( Yগ'= সি - 1, লেহারের কাগজ থেকে আসল সংজ্ঞা। এটা আমাদের precompute এটি একটি সহজ উপায় দেয়φ(Y,গ)মান বৃদ্ধিরগ।ϕ ( y), গ ) = ϕ ( y ), সি - 1 ) - ϕ ( y )পিগ, সি - 1 )ϕ ( y), গ )গ
Precomputing জন্য উপরন্তু একটি নির্দিষ্ট, কম মান গ , আমরা এটা কম মানের জন্য precompute করব Y , পুনরাবৃত্তির একটি নির্দিষ্ট থ্রেশহোল্ড নিচে পড়ে সংক্ষিপ্ত কাটা।ϕ ( y), গ )গY
বাস্তবায়ন
পূর্ববর্তী বিভাগটি কোডের বেশিরভাগ অংশকে কভার করে। একটি অবশিষ্ট, গুরুত্বপূর্ণ বিশদটি হল কীভাবে ফাংশনে বিভাগগুলি Phi
সম্পাদন করা হয়।
যেহেতু গণনা কেবল প্রথম π ( 3 by দ্বারা বিভাজক করা প্রয়োজন √φসংখ্যা, আমরাপরিবর্তে ফাংশনটিব্যবহার করতে পারি। বরং কেবল একটি বিভাজক চেয়েYএকটি মৌলিক দ্বারাপি, আমরা গুনYদ্বারাঘপি≈ 2 64π( এক্স--√3)fastdiv
YপিY পরিবর্তে এবংyপুনরুদ্ধারঘপি। 264পি asdpyYপি । X64-তে কীভাবে পূর্ণসংখ্যারগুণনটিপ্রয়োগ করাহয় তার জন্য264 দিয়েবিভাজক করাপ্রয়োজন হয় না; উচ্চতর 64 বিটঘপিYতাদের নিজস্ব রেজিস্টার সংরক্ষিত হয়।ঘপিY264264ঘপিY
নোট করুন যে এই পদ্ধতির জন্য প্রিকোমপুটিং প্রয়োজন , যা y গণনার চেয়ে দ্রুত নয়ঘপি সরাসরি। তবে, যেহেতু আমাদের বার বার একই প্রাইম দ্বারা ভাগ করতে হয় এবং বিভাগটিগুণনের চেয়েঅনেক ধীরহয়, ফলস্বরূপ একটি গুরুত্বপূর্ণ গতিবেগ আসে। এই অ্যালগরিদমের আরও বিশদ, পাশাপাশি একটি আনুষ্ঠানিক প্রমাণওবিভাগে ইনভেরিয়েন্ট ইন্টিজার্স দ্বারা গুণক ব্যবহার করেপাওয়া যাবে।Yপি