素数筛选法

素数筛选有两种,一种是普通的筛选,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;
}

 

 

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值