2012-02-20 74 views
3

我試圖解決問題PRIME1使用Eratosthenes分割篩。我的程序可以正常使用正常篩網,最高可達NEW_MAX。但是在分段篩分發揮作用的情況下,有一些問題n > NEW_MAX。在這種情況下,它只是打印所有的數字。這裏是鏈接到代碼相關的測試用例:http://ideone.com/8H5lK#view_edit_boxSpoj PRIME1使用Eratosthenes篩(在C)

/* segmented sieve */ 
#include <math.h> 
#include <stdio.h> 
#include <stdlib.h> 
#define MAX_LIMIT 1000000000 //10^9 
#define NEW_MAX 31623 /*SQUARE ROOT OF 1000000000*/ 
#define MAX_WIDTH 100000 //10^5 
char flags[NEW_MAX+100]; /*TO PREVENT SEGMENTATION FAULT*/ 

void initialise(char flagarr[], long int n) //initialise all elements to true from 1 to n 
{ 
    long int i; 
    for (i = 1; i <= n; i++) 
     flagarr[i] = 't'; 
} 

void sieve(unsigned long long m, unsigned long long n, char seg_flags[]) 
{ 
    unsigned long long p, i, limit; 
    if (m == 1) 
     seg_flags[1] = 'f'; 
    /*Seperate inner loop for p=2 so that evens are not iterated*/ 
    for (i = 4; i >= m && i <= n; i += 2) 
    { 
     seg_flags[i-m+1] = 'f'; 
    } 
    if (seg_flags == flags) 
     limit = NEW_MAX; 
    else 
     limit = sqrt(n); 
    for (p = 3; p <= limit+1; p += 2) //initial p+=2 bcoz we need not check even 
    { 
     if (flags[p] == 't') 
     { 
      for (i = p*p; i >= m && i <= n; i += p) //start from p square since under it would have been cut 
      seg_flags[i-m+1] = 'f';   /*p*p, p*p+p, p*p + 2p are not primes*/ 
     } 
    } 
} 

void print_sieve(unsigned long long m,unsigned long long n,char flagarr[]) 
{ 
    unsigned long long i; 
    if (flags == flagarr) //print non-segented sieve 
    { 
     for (i = m; i <= n; i++) 
      if (flagarr[i] == 't') 
       printf("%llu\n", i); 
    } 
    else 
    { 
     //print segmented 
     for (i = m; i <= n; i++) 
      if (flagarr[i-m+1] == 't') 
       printf("%llu\n", i); 
    } 
} 

int main() 
{ 
    unsigned long long m, n; 
    int t; 
    char seg_flags[MAX_WIDTH+100]; 
    /*setting of flags for prime nos. by sieve of erasthromas upto NEW_MAX*/ 
    initialise(flags, NEW_MAX); 
    flags[1] = 'f'; /*1 is not prime*/ 
    sieve(1, NEW_MAX, flags); 
    /*end of initial sieving*/ 
    scanf("%d", &t); 
    while (t--) 
    { 
     scanf("%llu %llu", &m, &n); 
     if (n <= NEW_MAX) 
      print_sieve(m, n, flags); //NO SEGMENTED SIEVING NECESSARY 
     else if (m > NEW_MAX) 
     { 
      initialise(seg_flags, n-m+1); //segmented sieving necessary 
      sieve(m, n, seg_flags); 
      print_sieve(m, n, seg_flags); 
     } 
     else if (m <= NEW_MAX && n > NEW_MAX) //PARTIAL SEGMENTED SIEVING NECESSARY 
     { 
      print_sieve(m, NEW_MAX, flags); 
      /*now lower bound for seg sieving is new_max+1*/ 
      initialise(seg_flags, n-NEW_MAX); 
      sieve(NEW_MAX+1, n, seg_flags); 
      print_sieve(NEW_MAX+1, n, seg_flags); 
     } 
     putchar('\n'); 
    } 
    system("pause"); 
    return 0; 
} 

更新:感謝您的FR響應丹尼爾。我實現了一些烏爾建議,我的代碼現在產生正確的輸出: -

/*segmented sieve*/ 
#include<math.h> 
#include<stdio.h> 
#include<stdlib.h> 
#define MAX_LIMIT 1000000000 /*10^9*/ 
#define NEW_MAX 31623 /*SQUARE ROOT OF 1000000000*/ 
#define MAX_WIDTH 100000 /*10^5*/ 
int flags[NEW_MAX+1]; /*TO PREVENT SEGMENTATION FAULT goblal so initialised to 0,true*/  

void initialise(int flagarr[],long int n) 
/*initialise all elements to true from 1 to n*/ 
{ 
    long int i; 
    for(i=3;i<=n;i+=2) 
     flagarr[i]=0; 
} 

void sieve(unsigned long m,unsigned long n,int seg_flags[]) 
{ 

    unsigned long p,i,limit; 

    /*Seperate inner loop for p=2 so that evens are not iterated*/ 
    if(m%2==0) 
     i=m; 
    else 
     i=m+1; 
    /*i is now next even > m*/ 
    for(;i<=n;i+=2) 
    { 

     seg_flags[i-m+1]=1; 

    } 
    if(seg_flags==flags) 
     limit=NEW_MAX; 
    else 
     limit=sqrt(n); 
    for(p=3;p<=limit+1;p+=2) /*initial p+=2 bcoz we need not check even*/ 
    { 
     if(flags[p]==0) 
     { 


      for(i=p*p; i<=n ;i+=p) 
      /*start from p square since under it would have been cut*/ 
      { 
       if(i<m) 
        continue; 
       seg_flags[i-m+1]=1; 
        /*p*p, p*p+p, p*p + 2p are not primes*/ 

      } 
     } 
    } 
} 

void print_sieve(unsigned long m,unsigned long n,int flagarr[]) 
{ 
    unsigned long i; 
    if(m<3) 
    {printf("2\n");m=3;} 
    if(m%2==0) 
     i=m+1; 
    else 
     i=m; 
if(flags==flagarr) /*print non-segented sieve*/ 
{ 
    for(;i<=n;i+=2) 
     if(flagarr[i]==0) 
       printf("%lu\n",i); 
} 
else { 
//print segmented 

    for(;i<=n;i+=2) 
     if(flagarr[i-m+1]==0) 
       printf("%lu\n",i); 
}} 

int main() 
{ 
    unsigned long m,n; 
    int t; 
    int seg_flags[MAX_WIDTH+100]; 
    /*setting of flags for prime nos. by sieve of erasthromas upto NEW_MAX*/ 
    sieve(1,NEW_MAX,flags); 
    /*end of initial sieving*/ 
    scanf("%d",&t); 
    while(t--) 
    { 
     scanf("%lu %lu",&m,&n); 
     if(n<=NEW_MAX) 
      print_sieve(m,n,flags); 
      /*NO SEGMENTED SIEVING NECESSARY*/ 
     else if(m>NEW_MAX) 
     { 
      initialise(seg_flags,n-m+1); 
      /*segmented sieving necessary*/ 
      sieve(m,n,seg_flags); 
      print_sieve(m,n,seg_flags); 
     } 
     else if(m<=NEW_MAX && n>NEW_MAX) 
     /*PARTIAL SEGMENTED SIEVING NECESSARY*/ 
     { 
      print_sieve(m,NEW_MAX,flags); 
      /*now lower bound for seg sieving is new_max+1*/ 
      initialise(seg_flags,n-NEW_MAX); 
      sieve(NEW_MAX+1,n,seg_flags); 
      print_sieve(NEW_MAX+1,n,seg_flags); 
     } 
     putchar('\n'); 
    } 
    system("pause"); 
    return 0; 
} 

但我篩功能下面將進一步考慮到烏拉圭回合的其他建議產生不正確的輸出: -

void sieve(unsigned long m,unsigned long n,int seg_flags[]) 
{ 

     unsigned long p,i,limit; 
     p=sqrt(m); 
     while(flags[p]!=0) 
       p++; 
     /*we thus get the starting prime, the first prime>sqrt(m)*/ 

     if(seg_flags==flags) 
       limit=NEW_MAX; 
     else 
       limit=sqrt(n); 
     for(;p<=limit+1;p++) /*initial p+=2 bcoz we need not check even*/ 
     { 
       if(flags[p]==0) 
       { 
         if(m%p==0) /*to find first multiple of p>=m*/ 
           i=m; 
         else 
           i=(m/p+1)*p; 

         for(; i<=n ;i+=p) 
         /*start from p square since under it would have been cut*/ 
         { 

           seg_flags[i-m+1]=1;  
             /*p*p, p*p+p, p*p + 2p are not primes*/ 

         } 
       } 
     } 
} 
+1

選擇的代碼和使用Ctrl-K – 2012-02-20 13:54:43

回答

2

你的問題是

for (i = 4; i >= m && i <= n; i += 2) 
for (i = p*p; i >= m && i <= n; i += p) 

如果範圍的下限爲4或更小,且您只消除大於的素數的倍數,則您只消除2的倍數。從環路狀態中刪除i >= m部分,並將其替換爲環路體中的if (i < m) { continue; }(更好的是,直接計算p的第一個倍數不小於m,並完全避免該條件)。

而是採用't''f'爲標誌,應使用10爲DMR有意和,將被更好地理解。

再更新:此

/*Seperate inner loop for p=2 so that evens are not iterated*/ 
if(m%2==0) 
    i=m; 
else 
    i=m+1; 
/*i is now next even > m*/ 
for(;i<=n;i+=2) 

會如果m == 2傷害你。如果m == 2,則必須以i = 4開頭。

關於

unsigned long p,i,limit; 
p=sqrt(m); 
while(flags[p]!=0) 
    p++; 
/* snip */ 
for(;p<=limit+1;p++) 

看來你誤解了我,「你只消除素數比sqrt(m)更大的倍數」並不意味着你不必消除小素數的倍數,這意味着你的初始版本沒有,結果幾乎所有的數字都沒有被消除。您應該使用p = 2開始外部循環 - 或者對於2的倍數分別進行傳遞,並使用3開始該循環,將p增加2,然後在p*pp的較大倍數處開始內部循環不小於m。您對後者的作品代碼,因此它包裹在

if ((i = p*p) < m) { 
    /* set i to smallest multiple of p which is >= m */ 
} 

將工作(你可以把它快一點,避免了分公司,並只使用一個師,但差異將是微不足道的)。最後,你用0和1代表的選擇是不規範的,這是C,所以0在條件和其他所有條件下評估爲真,所以規範替換應該是't' -> 1'f' -> 0,並且在上下文中此,這裏的陣列條目是標誌,一個將檢查

if (flags[p]) // instead of: if (flags[p] != 0) 
if (!flags[p]) // instead of: if (flags[p] == 0) 

也沒有必要從char[]改變數組類型int[]char是整數類型也一樣,所以0和1是完全沒有char值。數組類型的選擇具有性能影響。一方面,int大小的加載和存儲通常比字節大小更快,所以對於字大小的讀取和寫入將傾向於int flags[]甚至long int flags[]。另一方面,較小的char flags[]可以獲得更好的緩存局部性。使用每個標誌位一位,你會得到更好的緩存局部性,但這會使讀/設置/清除標誌更慢。取得最佳性能取決於篩的結構和尺寸。

+0

感謝名單,請參見上面我的問題編輯! – ksb 2012-02-20 19:15:05

+0

相應地更新了答案。 – 2012-02-20 20:11:34

+1

感謝你的巨大幫助,終於設法得到了一個ac @ spoj。 – ksb 2012-02-21 16:27:50