素数筛选有两种,一种是普通的筛选,O(nloglogn)
依据是:
1).若X是素数,则X的倍数都不是素数
2).若X不是素数,则X肯定在[1,X)之间被筛选掉。即存在Y,使得k*Y=X,然后Y根据推理1) 判定X为非素数。
=》若X未被事先筛选掉则X为素数
由当前的去筛选后面的
Java版
public void countPrimes(int n) {
boolean[] notPri = new boolean [n+1]; //默认false,代表默认所有数都是素数
int cnt = 0;
for (int i = 2; i * i <= n; i++) {
if(!notPri[i]){ //如果i是素数,那么剔除i的各种倍数
for (int j = i*2; j <= n; j += i) {
notPri[j] = true; //标记为不是素数
}
}
}
// notPri[i] 如果为true: 代表i不是素数
}
C++版
#include <cstdio>
#include <string>
#include <iostream>
using namespace std;
const int maxn = 10000050;
//普通筛选n以内的素数 O(nloglogn)
int notPrime[maxn] = {0}; //0表示素数,1为非素数
void getPrime(int n)
{
int i;
int t;
for (i = 2; i * i <= n; i++)
{
if (!notPrime[i])
{
for (t = 2 * i; t <= n; t += i)
{
notPrime[t] = 1;
}
}
}
}
第二种是线性筛选,近似于O(n)的复杂度
//线性筛选 近似于O(n)
bool isp[maxn]; //isp[i]:i是否为素数,0/false为素数
int p[maxn]; //存素数
int pNum=0;
void getPrime2(int n)
{
for (int i = 2; i <= n; i++)
{
if (!isp[i]) p[pNum++] = i;
for (int j = 0; j < pNum && i * p[j] <= n; j++)
{
isp[ i * p[j] ] = 1; //把i之前的所有素数p[j]的i倍 筛掉
if( !(i % p[j]) ) //如果i整除某个p[j],则停止
break;
}
}
}
快在对 I = 合数时的处理
大概思路是对于一个数I(素数或合数),筛掉 小于这个数的所有素数J 的 I 倍,当J是 I 的约数时( I 是J 的倍数,此时 I 肯定为合数)不再筛选(J+1) * I,(J+2) * I...)
因为: 一个合数 与 一个素数的乘积 可以表示为 更大的一个合数 与 更小的一个素数 的乘积(任一个合数都可以等于若干的素数乘积,且两个素数乘积为合数。原本的合数分解为更小的素数X和一个素数Y,此时Y乘 原式的素数的乘积 就成了个更大的合数)
如 I = 15时,15之前的素数是2,3,5,7,11,13
那么再筛掉2*15后发现15%3==0,那么把15*3也筛掉,就不再筛选15*7,15*11,15*13了。
比如15*7:因为15=3*5,则15*7 == 3*5*7 == 35 * 3【 I = 35时筛选J = 3】发现I = 35时会筛掉35*3,那么现在就不筛15*7了。
事实证明,100W以下的数据,普通筛选优于线性筛选。
完整测试代码
#include <cstdio>
#include <string>
#include <iostream>
using namespace std;
const int maxn = 10000050;
//普通筛选n以内的素数 O(nloglogn)
int prime[maxn] = {0}; //0表示素数,1为非素数
void getPrime(int n)
{
int i;
int t;
for (i = 2; i * i <= n; i++)
{
if (!prime[i])
{
for (t = 2 * i; t <= n; t += i)
{
prime[t] = 1;
}
}
}
}
//线性筛选 近似于O(n)
bool isp[maxn]; //isp[i]:i是否为素数,0/false为素数
int p[maxn]; //存素数
int pNum=0;
void getPrime2(int n)
{
for (int i = 2; i <= n; i++)
{
if (!isp[i]) p[pNum++] = i;
for (int j = 0; j < pNum && i * p[j] <= n; j++)
{
isp[ i * p[j] ] = 1; //把i之前的所有素数p[j]的i倍 筛掉
if( !(i % p[j]) ) //如果i整除某个p[j],则停止
break;
}
}
}
void show(bool *p,int n)
{
for(int i=2;i<=n;i++)
{
if(!p[i])
printf("%d ",i);
}
}
int main()
{
int n=100000;
// getPrime(n);
getPrime2(n);
// show(isp,n);
return 0;
}