几种素数筛法
素数的求解是数论题目中频繁遇到的问题,下面介绍几种求 n 以内的素数的算法
全局定义:
1 const int n = ?; //n范围 2 const int ma = ?; //素数个数 3 bool nu[n]; //标记数组 4 int phi[ma]; //存素数 5
1、较为高效的筛法
思想:从 2 开始,即标记数组为0(或为1),当标记数组为 0(或1) 时,该数为素数,把素数的倍数筛掉。此算法缺陷在于会重复筛选有不同素数因子的合数(比如说6,被2筛一次,又被3筛一次)
代码:
1 void prime1() //因为标记的时候会出现大量重复 2 { 3 memset(nu, 0, sizeof(nu)); 4 int k = 0; 5 for(int i = 2; i < n; ++i) 6 if(!nu[i]) 7 { 8 phi[k++] = i; 9 for(int j = i*i; j < n; j += i) 10 nu[j] = 1; 11 } 12 }
2、线性筛法
线性筛法就是为了避免重复筛掉同一个合数。思想是:从2 开始遍历,遇到素数就记录下来,当 i 遍历到为前面已经存储的素数的倍数的时候,就跳出循环,这样不仅可以筛选出所有素数,而且避免了重复。
代码:
1 void prime2() //线性筛法 2 { 3 memset(nu, 0, sizeof(nu)); 4 int k = 0; 5 for(int i = 2; i < n; ++i) 6 { 7 if(!nu[i]) phi[k++] = i; 8 for(int j = 0; (j<k && i*phi[j]<n ); ++j) 9 { 10 nu[i*phi[j]] = 1; 11 if(i%phi[j] == 0) break; 12 } 13 } 14 }
优化:
优化原理:除了2以外,所有素数都是奇数,所以排除2以后,i 就可以不管偶数了,值得注意的是,排除 2 之后,nu[4] 要清除标记。
代码:
1 void prime3() //线性筛法改良 2 { 3 memset(nu, 0, sizeof(nu)); 4 int k = 1; 5 phi[0] = 2, nu[4] = 1; 6 for(int i = 3; i < n; i += 2) 7 { 8 if(!nu[i]) phi[k++] = i; 9 for(int j = 0; (j<k && i*phi[j]<n); ++j) 10 { 11 nu[i*phi[j]] = 1; 12 if(i%phi[j] == 0) break; 13 } 14 } 15 }