普通筛素数
素数的因数只有1和它本身
根据这个定义,有个筛素数这种方法
当我们想找小于30的素数的时候,可以从小到大遍历
当遍历到2的时候,2就是素数,而2的倍数将被筛去
剩余最小的数的是3,3就是素数,而3的倍数将被筛去
剩余最小的数是5(4已被筛去),再筛去5的倍数
直到最后,每次最小的数就是素数,而它的倍数将被筛去
每次找到的最小数,因为没有被筛去,说明前面没有他的因数,说明最小数一定是素数且素数一定不会被筛掉
c++代码
#include "iostream"
#include "cstring"
using namespace std;
int main(int argc, char const *argv[])
{
int s=0;
bool IsPrime[100];
memset(IsPrime,1,sizeof(IsPrime));
for(int i=2;i<100;i++){
if(IsPrime[i]){
cout << i << ' ';
for(int j=2;j*i<100;j++){
IsPrime[i * j]=false;
s++;
}
}
}
cout << endl << s;
return 0;
}
线性筛素数
在前面的筛素数方法中会产生重复筛的问题,比如12在2时筛了一次,在3时又筛了一次,这样就大大降低了效率.
因此我们需要一种方法使得每个合数只被筛一次
可以看出
12=43
4=22
因此12=223=2*6
也就是说每个合数都可以被分解成唯一的最小的质数和另一个数的乘积的形式,因此12应当在2时被晒而不是3
和上面的普通筛不同,普通筛是筛去素数的k倍(k=2,3,4,5...)
线性筛是筛去i的素数倍,此时就可以判断,如果i%prime[j]==0
此时应当break
否则就会重复筛
例如:
当i=8时,i%prime[0]=8%2==0
,此时筛去2*8=16
后应当break
如果继续筛3*8=24
因为8可以被2整除,因此24=8*3=12*2
,24应当在i=12时被筛
因此我们建立两个数组,prime[]
保存素数,isNotPrime[i]
指示i是否是素数,num_prime
保存素数的个数
C++代码
#include <iostream>
using namespace std;
const int N = 100;
int prime[N] = {0},num_prime = 0;
int isNotPrime[N] = {1, 1};
int main()
{
for(int i = 2 ; i < N ; i ++){
if(! isNotPrime[i]) //i是素数
prime[num_prime ++]=i;
//遍历小于i的所有素数
for(int j = 0 ; j < num_prime && i * prime[j] < N ; j++){
isNotPrime[i * prime[j]] = 1; //筛去i的素数次倍
if( !(i % prime[j] ) ) //i可以被prime[j]*k代替,可以留到i=prime[j+1]*(i/prime[j])时再筛
break;
}
}
for(int i=0;i<num_prime;i++) cout << prime[i] << ' ';
return 0;
}