1000億以内素数カウントアルゴリズム



転載先:
とても良い文章で、効率はかなり高くて、残念ながら注釈が少なくて、少し怒っているように見えます
 
1000億以内素数カウントアルゴリズム
 
/******************************************************************
copyright (C) 2007 Huang Yuanbing
version 1.1, 2007 PrimeNumber
mailto: [email protected] (remove the last digit 1 for "laji" mail)
free use for non-commercial purposes
******************************************************************

  main ideal come from a paper by M.DELEGLISE ADN J.LAGARIAS
  "COMPUTING PI(x): THE MEISSEL, LEHMER, LAGARIAS, MILLER, ODLYZKO METHOD"

  a = PI(y); (y >>= x^(1/3) and y <= x^(1/2))

  PI(x)         = phi(x, a) + a - 1 - P2Xa(x,a);
  phi(x, a)     = S0 + S
  = S0  + S1 + S3 + S2
  = S0  + S1 + S3 + U + V
  = S0  + S1 + S3 + U + V1 + V2
  = S0  + S1 + S3 + U + V1 + W1 + W2 + W3 + W4 + W5
  it need O(n^2/3) space and MAXN > 10 and MAXN <= 1e11
**********************************************************************/

#i nclude <time.h>
#i nclude <stdio.h>
#i nclude <math.h>
#i nclude <memory.h>
#i nclude <assert.h>

#define COMP 4
#define MASKN(n)  (1 << ((n >> 1) & 7))
#define min(a, b) (a) < (b) ? (a) : (b)
#define MULTI_THREAD
#define THRED_NUMS 4
//#define PRINT_DEBUG

typedef unsigned int uint;
#ifdef _WIN32
typedef  __int64 int64;
#else
typedef long long int64;
#endif

int *Prime;
int *PI;

int64 MAXN = (int64)1e11;
int pt[130000];   // MAXN < 1e11, size = np

int *phiTable;
int *minfactor;

int x23, x12, x13, x14, y;
int64 x;

int ST = 7, Q = 1, phiQ = 1;
//Q    = 2*3*5*7*11*13 = 30030
//phiQ = 1*2*4*6*10*12 = 5760

int phi(int x, int a)
{
    if (a == ST)
        return (x / Q) * phiQ + phiTable[x % Q];
    else if (a == 0)
        return x;
    else if (x < Prime[a])
        return 1;
    else
        return phi(x, a - 1) - phi(x / Prime[a], a - 1);
}

#ifdef MULTI_THREAD
struct ThreadInfo{
    int pindex;
    int theadnums;
    int64 pnums;
}Threadparam[2 * THRED_NUMS + 2];

#ifdef _WIN32
#i nclude <windows.h>
DWORD WINAPI WIN32ThreadFun(LPVOID pinfo)
#else
#i nclude <pthread.h>
void* POSIXThreadFun(void *pinfo)
#endif
{
    ThreadInfo *pThreadInfo = (ThreadInfo *) (pinfo);
    int addstep = pThreadInfo->theadnums * 2;

    for (int i = pThreadInfo->pindex; pt[i] != 0; i += addstep){
        if (pt[i] > 0)
            pThreadInfo->pnums -= phi(pt[i], pt[i + 1]);
        else
            pThreadInfo->pnums += phi(-pt[i], pt[i + 1]);
    }
    //    printf("ThreadID = %4d, phi(%d) = %d
", GetCurrentThreadId(), pThreadInfo->pindex, pThreadInfo->pnums); // printf("ThreadID = %4d, phi(%d) = %d
", pthread_self(), pThreadInfo->pindex, pThreadInfo->pnums); return 0; } int64 multiThread(int theadnums) { int i; int64 pnums = 0; assert(theadnums < 2 * THRED_NUMS); for (i = 0; i < theadnums; i++){ Threadparam[i].pindex = 2 * i + 1; Threadparam[i].theadnums = theadnums; Threadparam[i].pnums = 0; } #ifdef _WIN32 HANDLE tHand[THRED_NUMS * 2]; DWORD threadID[THRED_NUMS * 2]; for (i = 0; i < theadnums; i++){ tHand[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WIN32ThreadFun, (LPVOID)(&Threadparam[i]), 0, &threadID[i]); if (tHand[i] == NULL) printf("create Win32 thread error
"); } WaitForMultipleObjects(theadnums, tHand, true, INFINITE); for (i = 0; i < theadnums; i++){ pnums += Threadparam[i].pnums; CloseHandle(tHand[i]); } #else pthread_t tid[THRED_NUMS]; for (i = 0; i < theadnums; i++){ int error = pthread_create(&tid[i], NULL, POSIXThreadFun, &Threadparam[i]); if ( error != 0 ) printf("Create pthread error: %d
", error); } for (i = 0; i < theadnums; i++){ pthread_join(tid[i], NULL); pnums += Threadparam[i].pnums; } #endif return pnums; } #endif int freememory(int alloc) { int memsize = (x / y) + 100; if (alloc == 0){ int psize = (int) (memsize / log(memsize)); //printf("psize = %d memeszie = %d
", psize, (int)(psize * 1.2) ); PI = new int [memsize + 100]; Prime = new int[(int)(psize * 1.2) + 100]; assert(PI && Prime); }else{ delete phiTable; delete minfactor; delete Prime; delete PI; } return alloc; } void init_phiTable( ) { clock_t start = clock(); int p, i, j; if (x < 1e10) ST = 6; if (ST > PI[y]) ST = PI[y]; for (i = 1; i <= ST; ++i){ Q *= Prime[i]; phiQ *= Prime[i] - 1; } phiTable = new int[Q + 10]; for (i = 0; i < Q; ++i) phiTable[i] = i; for (i = 1; i <= ST; ++i){ p = Prime[i]; for (j = Q - 1; j >= 0; --j) phiTable[j] -= phiTable[j / p]; } printf(" Q = %d, PhiQ = %d
", Q, phiQ); printf(" init_phiTable time = %ld ms
", clock() - start); } void init_minFactor( ) { clock_t start = clock(); int i, j, maxy = y + 10; int sqrty = (int)sqrt(maxy) + 1; minfactor = new int[maxy + 10]; for (i = 0; i < maxy; i++) minfactor[i] = i; for (i = 1; Prime[i] <= maxy; i++){ for (j = Prime[i]; j <= maxy; j += Prime[i]){ if (minfactor[j] == -j || minfactor[j] == j) minfactor[j] = -Prime[i]; else minfactor[j] = -minfactor[j]; } } for (i = 1; Prime[i] <= sqrty; i++){ int powp = Prime[i] * Prime[i]; for (j = powp; j <= maxy; j += powp) minfactor[j] = 0; } printf(" init_minFactor time = %ld ms
", clock() - start); } int sieve( ) { clock_t start = clock(); int primes = 1; int maxp = (x / y) + 10; Prime[1] = 2; unsigned char *bitMask = (unsigned char *) PI; memset(bitMask, 0, (maxp + 64) >> COMP); for (int p = 3; p < maxp; p += 2){ if ( !(bitMask[p >> COMP] & MASKN(p)) ){ Prime[++primes] = p; if (p > maxp / p) continue; for (int j = p * p; j < maxp; j += p << 1) bitMask[j >> COMP] |= MASKN(j); } } Prime[0] = primes; Prime[primes] = 0; printf("pi(%d) = %d
", maxp, primes); printf(" sieve time = %ld ms
", clock() - start); return primes; } void init_x( ) { x = (int64)MAXN; x23 = (int)(pow(x, 2.0 / 3) + 0.01); x12 = (int)(pow(x, 1.0 / 2) + 0.01); x13 = (int)(pow(x, 1.0 / 3) + 0.01); x14 = (int)(pow(x, 1.0 / 4) + 0.01); assert((int64)x12 * x12 <= x); assert((int64)x13 * x13 * x13 <= x); assert((int64)x14 * x14 * x14 * x14 <= x); y = x13; printf(" x14 = %d, x13 = %d, x12 = %d x23 = %d, y = %d
", x14, x13, x12, x23, y); freememory(0); } int64 cal_S0( ) { int64 S0 = x; for (int j = 2; j <= y; j++){ if (minfactor[j] > 0) S0 += x / j; else if (minfactor[j] < 0) S0 -= x / j; } printf("S0 = %I64d
", S0); return S0; } // so bad performance in this function int64 cal_S3( ) { clock_t start = clock(); int i, p, a; int np = 1; int64 S3 = 0; for (i = 1; i <= PI[x14]; i++){ p = Prime[i]; a = PI[p] - 1; #ifdef PRINT_DEBUG assert(p <= x14 && a >= 0); #endif for (int m = y / p + 1; m <= y; m++){ int xx = x / (int64)(m * p); #ifndef MULTI_THREAD if (minfactor[m] > p) S3 -= phi(xx, a); else if (minfactor[m] < -p) S3 += phi(xx, a); #else if (minfactor[m] > p){ pt[np++] = xx; pt[np++] = a; } else if (minfactor[m] < -p){ pt[np++] = -xx; pt[np++] = a; } #endif } } #ifdef MULTI_THREAD printf("np = %d
", np); if (np < 100) S3 = multiThread(1); else S3 = multiThread(THRED_NUMS); #endif printf("S3 = %I64d
", S3); printf(" cal S3 time = %ld ms
", clock() - start); return S3; } void init_PI( ) { clock_t start = clock(); int Px = 1; PI[0] = PI[1] = 0; for (int i = 1; Prime[i]; i++, Px++){ for (int j = Prime[i]; j <= Prime[i + 1]; j++) PI[j] = Px; } //printf(" Px = %d, primes = %d
", Px, Prime[0]); printf(" PI[%d] = %d
", Px, PI[Px - 1]); printf(" init_PI time = %ld ms
", clock() - start); } int64 cal_P2xa( ) { int64 phi2 = (int64)PI[y] * (PI[y] - 1) / 2 - (int64)PI[x12] * (PI[x12] - 1) / 2; for (int i = PI[y] + 1; i <= PI[x12] + 0; i++){ int p = Prime[i]; #ifdef PRINT_DEBUG assert(p > y && p <= x12); #endif phi2 += PI[x / p]; } printf("P2xa(%I64d, %d) = %I64d
", x, PI[y], phi2); return phi2; } int64 cal_S1( ) { int64 temp = PI[y] - PI[x13]; int64 S1 = temp * (temp - 1) / 2; printf("S1 = %I64d
", S1); return S1; } int64 cal_U( ) { int64 p, U = 0; int sqrt_xdivy = (int)sqrt(x / y); for (int i = PI[sqrt_xdivy] + 1; i <= PI[x13]; i++){ p = Prime[i]; U += PI[y] - PI[x / (p * p)]; } printf("U = %I64d
", U); return U; } int64 cal_V1( ) { int64 V1 = 0; int MINq, p; for (int i = PI[x14] + 1; i <= PI[x13]; i++){ p = Prime[i]; MINq = min((uint)y, x / ((int64)p * p)); #ifdef PRINT_DEBUG assert(p > x14 && p <= x13 && MINq >= p); #endif V1 += (int64)(2 - PI[p]) * (PI[MINq] - PI[p]); //!!!!!! } printf("V1 = %I64d
", V1); return V1; } int64 cal_V2( ) { int64 V2 = 0; int i, sqrt_xdivy = (int)sqrt(x / y); // int sqrt_xdivy2 = (int)sqrt(x / (y * y)); // int sqrt_xdivp = (int)sqrt(x / 1); int xdivy2 = x / (y * y); #if 0 uint p, q; uint W[6] = {0}; //W1 for (p = x14 + 1; p <= xdivy2; p++) for (q = p + 1; q <= y; q++) W[1] += PI[x / (p * q)]; //W2 for (p = xdivy2 + 1; p <= sqrt_xdivy; p++){ sqrt_xdivp = (uint)sqrt(x / p); for (q = p + 1; q <= sqrt_xdivp; q++) W[2] += PI[x / (p * q)]; } //W3 for (p = xdivy2 + 1; p <= sqrt_xdivy; p++) for (q = sqrt(x / p) + 1; q <= y; q++) W[3] += PI[x / (p * q)]; //W4 for (p = sqrt_xdivy + 1; p <= x13; p++){ sqrt_xdivp = (uint)sqrt(x / p); for (q = p; q <= sqrt_xdivp; q++) W[4] += PI[x / (p * q)]; } //W5 for (p = sqrt_xdivy + 1; p <= x13; p++) for (q = sqrt(x / p) + 1; q <= xdivy2; q++) W[5] += PI[x / (p * q)]; V2 = W[1] + W[2] + W[3] + W[4] + W[5]; #else for (i = PI[x14] + 1; i <= PI[sqrt_xdivy]; i++){ int64 p = Prime[i]; #ifdef PRINT_DEBUG assert(p > x14 && p <= sqrt_xdivy); #endif for (int j = PI[p] + 1; j <= PI[y]; j++){ int q = Prime[j]; #ifdef PRINT_DEBUG assert(q > p && q <= y); #endif V2 += PI[x / (p * q)]; } } for (i = PI[sqrt_xdivy] + 1; i <= PI[x13]; i++){ int64 p = Prime[i]; #ifdef PRINT_DEBUG assert(p > sqrt_xdivy && p <= x13); #endif xdivy2 = x / (p * p); for (int j = PI[p] + 1; j <= PI[xdivy2]; j++){ int q = Prime[j]; V2 += PI[x / (p * q)]; } } #endif printf("V2 = %I64d
", V2); return V2; } int main(int argc, char* argv[]) { clock_t start = clock(); if (argc > 1) MAXN = atoll(argv[1]); //mingw init_x( ); sieve( ); init_PI( ); init_minFactor( ); init_phiTable( ); int64 pix = PI[y] - 1; pix += cal_S3( ); //mul thread pix -= cal_P2xa( ); pix += cal_S0( ); if (y != x13){ pix += cal_S1( ); pix += cal_U( ); } pix += cal_V2( ); pix += cal_V1( ); #if 0 printf("phi(%u, 209) = %d, time = %ld ms
", x, phi(x, 209), clock() - start); delete phiTable; return 0; #endif puts("
Pi(x) = phi(x, a) + a - 1 - phi2(x,a):"); printf("pi(%I64d) = %I64d, time use %ld ms
", x, pix, clock() - start); freememory(1); return 0; } //benchmark, Windows Xp SP2, 1G, Mingw G++ 3.4.5 // pi( 2147483647) = 105097565, time use 108 ms (machine AMD 3600+ 2.0G) // pi(100000000000) = 4118054813, time use 2171 ms (machine intel PD 940 3.2G)

最後に論文を添付します.....
 
次はCSDNの上にもう1篇の比較的に細かい文章が素数を分析します:
http://blog.csdn.net/hexiios/article/details/4400068
下にも貼って、みんなで見学
 
素数を探して自然数Nより小さいすべての素数を求めます.
ソリューション
プログラム1-1クラシックアルゴリズム
古典的な素数判定アルゴリズムは、正の整数nを与え、2〜sqrt(n)の間のすべての整数でnを除去し、除去可能であればnは素数ではなく、除去できなければnは素数である.したがって、Nより小さいすべての素数を求めるプログラムは以下の通りである.
 
#include 
#include 
 
#define N 1000000
 
int main(int argc, char *argv[])
{
  int m, n;
  for (n =2 ; n < N; n++)
  {
      for (m = 2; m * m <= n; m++)
           if (n % m == 0) break;
      if (m * m > n) printf("%6d",n);
  }
  system("PAUSE"); 
  return 0;
 
アルゴリズムの時間的複雑さはO(N*sqrt(N))であり,解の規模の小さい入力は問題ない.しかし規模の大きいNに対しては,アルゴリズムは力不足である.エラドセふるい(sieve of Eratosthenes)というアルゴリズムがあり,解く際にかなりの効率を有するが,より多くの空間を犠牲にする.
 
プログラム1-2アラマルチプラグふるいアルゴリズム
このプログラムの目標は,iが素数であればa[i]=1を設定することである.素数でない場合は0に設定します.まず、すべての配列要素を1に設定し、既知の非素数がないことを示します.次に、非素数(既知の素数の倍数)として知られているインデックスに対応する配列要素を0に設定し、すべての小さい素数の倍数を0に設定した後もa[i]が1のままであれば、探した素数であると判断することができる.
 
#include 
#include 
 
#define N 1000000
 
int a[N];
 
int main(int argc, char *argv[])
{
  int i, j;
  for (i = 2; i < N; i++) a[i] = 1;
  for (i = 2; i < N; i++)
  {
      if (a[i])
         for (j = i + i; j < N; j += i)
             a[j] = 0;
  }
  for (i =2; i < N; i++)
      if (a[i]) printf("%6d ",i);
  system("PAUSE"); 
  return 0;
}
 
たとえば、32未満の素数を計算すると、まずすべての配列項目を1(次の2列目)に初期化し、非素数が発見されるごとの数を表します.次に、2、3、5倍のインデックスを持つ配列項目を0に設定します.2、3、5倍の数が非素数であるためです.1に保持されている配列項目は、素数(次の右列)に対応するインデックスを付けます.
i                2       3       5       a[i]
2       1                                   1
3       1                                   1
4       1       0                         
5       1                                   1
6       1       0      
7       1                                   1
8       1       0
9       1                 0
10     1       0
11     1                                   1
12     1       0
13     1                                   1
14     1       0
15     1                 0
16     1       0
17     1                                   1
18     1       0
19     1                                   1
20     1       0
21     1                 0
22     1       0
23     1                                   1
24     1       0
25     1                          0
26     1       0
27     1                 0
28     1       0
29     1                                   1
30     1       0
31     1                                   1
 
エラドセふるいアルゴリズムをどのように理解しますか?
小学校の時に学んだ素数と合数の性質を覚えているに違いない.いずれの合数もいくつかの素数の積として表すことができる.たとえば、4=2*2、6=2*3、12=2*2*3です.すなわち、合数Nは必ずNより小さい(またはいくつかの)素数の整数倍であり、逆に、Nがそれより小さい素数の倍数でなければ、Nは必然的に素数である.
 
プログラム1-3クラシックアルゴリズムの改良バージョン
古典的な素数判定アルゴリズムでは,2からsqart(n)までのすべての整数でnを除去する必要はなく,その間の素数だけで十分であり,合数が必ずいくつかの素数の積として表されるためである.アルゴリズムの改良バージョンは次のとおりです.
 
#include 
#include 
 
#define N 1000000
 
int prime[N];
 
int main(int argc, char *argv[])
{
    int i, n, flag;
    prime[0] = 0;//素数の合計を保存する
    for (n =2 ; n < N; n++)
    {
        flag = 1;
        for (i = 1; i <= prime[0] && prime[i] * prime[i] <= n; i++)
            if (n % prime[i] == 0)
            {
                flag = 0;
                break;
            }
        if (flag)
        {       
            prime[0]++;
            prime[prime[0]] = n;
            printf("%6d ",n);
        }
    }
    system("PAUSE");   
    return 0;
}
 
アルゴリズムは効率的に以前のバージョンより向上したが,決定された素数を記憶するために空間を犠牲にする必要がある.
 
プログラム1-4アラドプラグふるいアルゴリズムの改良版
プログラム1-2は、最も簡単な要素タイプ、0と1の2つの値を含む配列を使用し、整数の配列ではなくビットの配列を使用すると、より高い空間的有効性を得ることができます.
 
#include 
#include 
 
#define N 1000000
#define BITSPERWORD 32 
#define SHIFT 5
#define MASK 0x1F
#define COUNT 1+N/BITSPERWORD//Bit i対写数i
 
void set(int a[],int i);
void clr(int a[],int i);
int test(int a[],int i);
 
int  a[COUNT];
 
int main(int argc, char *argv[])
{
  int i, j;
  for (i = 0; i < COUNT; i++) a[i] = -1;
  for (i = 2; i < N; i++) {
      if (test(a,i))
         for (j = i + i; j < N; j += i)
             clr(a,j);
  }
  for (i =2; i < N; i++)
      if (test(a,i)) printf("%4d ",i);
  system("PAUSE"); 
  return 0;
}
 
void set(int a[],int i) {
    a[i >> SHIFT] |= (1<<(i & MASK));
}
 
void clr(int a[],int i) {
    a[i >> SHIFT] &= ~(1<<(i & MASK));
}
 
int test(int a[],int i) {
    return a[i >> SHIFT] & (1<<(i & MASK));
}