求小于N的所有素数
- 解法1:检查每一个数K,在2~sqrt(K)区间内是否含有它的因子。
- 解法2:我们可以按照从1到N的顺序来验证一个数是否为质数,这样当我们验证K的时候,所有小于K的质数都已经求得。那么我们没必要验证2~sqrt(K)区间内的有没有K的因子,我们只需要验证这个区间内的所有质数中有没有K的因子。
- 解法3:筛选法,先假设所有数都是质数,然后从2开始筛掉所有2的倍数,然后从没被筛掉的第一个数开始,继续筛选。每一个数被筛选了X次,X为它的质因子数目-2。
- 解法4:筛选法,在上述筛选法中每一个数被筛选了X次。例如6,它被2和3各筛选了1次。如何避免这种重复筛选呢?我们可以把一个合数分解为a×p,p为a的最小质因子,这样对于任何一个合数,就都有了一个唯一二元组(a,p)与之对应,我们每次都通过一个不同的(a,p)来覆盖,就可以保证没有重复的覆盖。也就是说循环的次数为小于N的合数的个数。
算法的效率4>3>2>1
#include<iostream> #include<algorithm> #include<math.h> using namespace std; //求素数表集中方法的比较 #define TABLE_LEN 20000000 #define PRIME_UP_BOUND 130000000 bool bPrime[PRIME_UP_BOUND]; int PrimeTable[TABLE_LEN]; int ptc; int LC; //循环计数器 void MakeTable1(int n) { int i,j,b; ptc = 0; PrimeTable[ptc++] = 2; for(int i = 3; i < n; i++) { b = 1; for(int j = 2; b && j * j <= i; j++) { LC++; b = i % j; } b ? PrimeTable[ptc++] = i : 0; } } void MakeTable2(int n) { int i,j,b; ptc = 0; PrimeTable[ptc++] = 2; for(int i = 3; i < n; i++) { b = 1; for(int j = 0; b && PrimeTable[j] * PrimeTable[j] <= i; j++) { LC++; b = i % PrimeTable[j]; } b ? PrimeTable[ptc++] = i : 0; } } void MakeTable3(int n) { int i,j; ptc = 0; memset(bPrime,1,sizeof(bPrime)); for( i = 2; i < n; i++) { if(bPrime[i]) { PrimeTable[ptc++] = i; for( j = i + i; j < n; j += i) { LC++; bPrime[j] = false; } } } } void MakeTable4(int n) { int i,j; ptc = 0; memset(bPrime,1,sizeof(bPrime)); for( i = 2; i < n; i++) { bPrime[i] ? PrimeTable[ptc++] = i : 0; for( j = 0; j < ptc && i * PrimeTable[j] < n; j++) { LC++; bPrime[ i * PrimeTable[j] ] = false; //当i%PrimeTable[j] == 0 的时候证明此时PrimeTable[j]为i的最小质因子,为了避免重复筛选,退出循环 if( i % PrimeTable[j] == 0) break; } } } int main() { int n; while(scanf("%d",&n)!=EOF) { LC = 0; MakeTable1(n); printf("%d %d\n",ptc,LC); LC = 0; MakeTable2(n); printf("%d %d\n",ptc,LC); LC = 0; MakeTable3(n); printf("%d %d\n",ptc,LC); LC = 0; MakeTable4(n); printf("%d %d\n",ptc,LC); } return 0; }
讨论:
方法4的复杂度为O(n),从渐进意义上讲应该没有更快的算法了吧,如果谁知道更好的方法请留言。
前三种方法的复杂度我不太会分析,如果有大牛会,望指教。