线性筛

筛素数是为了求得一个区间内的所有素数,而把不是素数的筛去。

http://www.cnblogs.com/zhuohan123/p/3233011.html

http://blog.csdn.net/leolin_/article/details/6642126

Eratosthennes筛法时间复杂度为O(nlogn),用每一个素数去筛它的倍数,即筛去它的合数,显然这样有些合数被重复筛了很多次,如果能保证每个合数只被筛一次(只被枚举一次),那就可以O(n)了。

一个定理:任何一个合数都可以表示成一个质数和一个数的乘积

这是显然的。用唯一分解定理就可以证明了。

线性筛(欧拉筛)就是利用了这个定理,对于每一个合数,我们只用它最小的质因子去筛它。为了达到这个目的,循环的顺序就至关重要。

我们先从小到大枚举所有数,然后再从小到大枚举所有已经找到的质数(这个质数自然<=这个数)

然后筛掉i*prime[j]。(一个数乘质数)

如果只是这样做那么时间复杂度为O(∑i/lni),不会算。。。只知道实际时间和Eratosthennes筛法差不多。

但是如果我们在第二层循环中加一句:

if(i%prime[j]==0)break;

不但正确性可以保证,而且时间复杂度降为O(n)。
正确性:

1、保证每个合数依然还是会被筛掉。

退出后,我们少筛了i*prime[j+k],1<=k,即没有用prime[j+k]去筛这个合数。

由    i%prime[j]==0    得:    i*prime[j+k]=(prime[j]*x)*prime[j+k]=prime[j]*(x*prime[j+k]),其中x是某个正整数。

即i*prime[j+k]这个合数可以被一个更小的质数prime[j]筛掉,所以以后枚举到更大的i时一定也会筛掉这个合数。

2、保证每个合数能被及时的筛掉。

这句话的意思是说,如果不能及时筛掉的话,我们就会误认为它是质数,然后把它误加入到素数表中。

如果这个数是个合数,根据唯一分解定理,一定能分解成多个n个素数的乘积,n>=2,那么这个合数之前肯定会被它最小的那个素因子筛掉。

时间复杂度:

每个合数会且仅会被最小的素因子筛去,而且只枚举一遍,所以总的时间复杂度为O(n)。

还有一些值得注意的东西:

虽然说Eratosthennes筛法时间复杂度为O(nlogn),但更准确的说法应该是Eratosthennes筛法时间复杂度介于O(n)和O(nlogn)之间,因为O(nlogn)的算法是不带任何优化的,优化后的代码在实战中效果很好,当n=1e6时,非线性部分带来的差距只有2~3倍。不带break语句的线性筛也是这个复杂度,即只比线性的增长快一点,只不过线性算法就是纯纯的O(n)了。详见紫书P312上时间复杂度的分析吧。

一些思考过程中用来分析的代码

一些思考过程中用来分析的代码

#include <cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const int maxn = 1e8;
int sb;
int cnt;
int prime[maxn],primesize,phi[maxn];
bool isprime[maxn];
void getlist(int listsize)
{
    memset(isprime,1,sizeof(isprime));
    isprime[1]=false;
    for(int i=2;i<=listsize;i++)
    {
        if(isprime[i])prime[++primesize]=i;
         for(int j=1;j<=primesize&&i*prime[j]<=listsize;j++)
         {
             cnt++;
             if(isprime[i*prime[j]]==false) sb++;
            isprime[i*prime[j]]=false;
            //if(i%prime[j]==0)break;
        }
        //puts("");
    }
    printf("计算量:%d\n",cnt);
    printf("重复数:%d\n",sb);
    printf("质数个数:%d\n",primesize);
}

void print()
{
    for(int i=0;i<primesize;i++) printf("%d\n",prime[i]);
}

void eratosthenes(int n)
{
    for(int i=2;i<n;i++) isprime[i]=1;
    int m=sqrt(n+0.5);
    for(int i=2;i<=m;i++)
    {
        if(isprime[i])
        {
            prime[++primesize]=i;
            for(int j=2*i;j<n;j+=i)
            {
                cnt++;
                if(isprime[j]==0) sb++;
                isprime[j]=0;
            }
        }
    }
    printf("计算量:%d\n",cnt);
    printf("重复数:%d\n",sb);
    printf("质数个数:%d\n",primesize);
}

int main()
{
    //getlist(1000000);
    eratosthenes(100000000);
    //print();
    return 0;
}

1、线性筛素数

for(int i=2;i<n;i++){
    if(!noprime[i])prime[++p]=i;
    for(int j=1;j<=p,i*prime[j]<n;j++){
        noprime[i*prime[j]]=1;
        if(!(i%prime[j]))break;
    }
}

2、线性筛欧拉函数

phi[1]=1;
for(rg int i=2;i<N;++i){
    if(!vis[i])pri[++top]=i,phi[i]=i-1;
    for(rg int j=1;j<=top&&i*pri[j]<N;++j){
        vis[i*pri[j]]=1;
        if(i%pri[j]==0){
            phi[i*pri[j]]=phi[i]*pri[j];
            break;
        }
        else phi[i*pri[j]]=phi[i]*(pri[j]-1)

3、线性筛莫比乌斯函数

u[1]=1;
for(rg int i=2;i<MX;++i){
    if(!is[i])pri[++tot]=i,u[i]=-1,g[i]=1;
    for(rg int j=1,k;j<=tot&&(k=i*pri[j])<MX;++j){
        is[k]=1;
        if(!(i%pri[j])){u[k]=0,g[k]=u[i];break;}
        else u[k]-=u[i],g[k]=u[i]-g[i];
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值