[数学/质数筛] 素数筛法

Date:2019/10/16??
其实现在都快1点了,算是16号,啊??
今天讲了素数的筛法

其实,在很小的数据范围内,不同的算法复杂度是差不多的,但是,如果处理几个数据,算法的作用就没有发挥出来

  1. 朴素算法
  2. 埃氏筛法
  3. 线性筛法(欧拉筛)
  4. P3383 【模板】线性筛素数

朴素算法

素数最朴素的算法了
它的时间复杂度是O(n*sqrt(n))

bool prime (int x){
	for(int i = 2; i <= x; i ++){
		if(x % i ==0){
			return 0;
		}}
	return 1;
}

但是,这只适用于小数据的处理,所以我们需要改进算法

Eratosthenes(埃氏筛法)

  • 整数的唯一分解定理:
    任何一个大于1的自然数 N,如果N不为质数,都可以唯一分解成有限个质数的乘积N=P1 ^ a1 · P2 ^ a2 · P3 ^ a3 · … · Pn ^ an ,这里P1<P2<P3<…<Pn均为质数,其诸指数 ai 是正整数。
    (:当然质数的话直接就是质数本身)

  • 埃氏筛法的思想:
    枚举每个素数,然后把他们的倍数都打上标记,从而达到筛出的目的
    质数的倍数一定不是质数

时间复杂度是 O(nloglogn)

具体代码是
  • 注意j = i可以做到一部分的优化作用
for(int i = 1; i <= sqrt(n); i ++){		//循环一遍可以有倍数的	
	if(b[i] == 1)	continue;			//如果不是质数或已经判断过,直接跳过 
	for(int j = i; i * j <= n;j++){		//内层循环倍数 //pay attention to j = i; 
		b[i*j] = 1;						//打上标记					
	}			
}	

但是这个算法在1e7左右还是不太好用,会TLE(~~哭晕~
而且埃氏筛法还有一个缺陷 :
对于一个合数,有可能被筛多次。例如 30 = 2 * 15 = 3 * 10 = 5*6……
那么如何确保每个合数只被筛选一次呢?我们只要用它的最小质因子来筛选即可,这便是欧拉筛法

线性筛法(欧拉筛法)

线性筛法是什么意思呢?
就是我们在埃氏筛法中有个问题就是一个数可能被筛多次
所以就造成了时间的浪费,所以算法就在这里做了改进

基本思想:在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的

int prime[maxn];
int visit[maxn];
void Prime(){
    mem(visit,0);
    mem(prime, 0);
    for (int i = 2;i <= maxn; i++) {
        if (!visit[i]) {
            prime[++prime[0]] = i;      //记录素数, 这个prime[0] 相当于 cnt,用来计数
        }
        for (int j = 1; j <= prime[0] && i*prime[j] <= maxn; j++) {
        //j 循环枚举了当前位置已判定为素数的数,并且限制倍数和质数的和的乘积不大于最大数
//            cout<<"  j = "<<j<<" prime["<<j<<"]"<<" = "<<prime[j]<<" i*prime[j] = "<<i*prime[j]<<endl;
            visit[i*prime[j]] = 1;    //打标记
            if (i % prime[j] == 0) {	//这一步比较重要,如果i%j==0,说明i 就不是最小的质因子,所以就跳出程序了;
                break;
            }
        }
    }
}

  • 还有网友版本的~
void init() 
{
    memset(bz,1,sizeof(bz));
    tot=0;
    for (int i=2;i<MAXN;i++) 
    {
        if (bz[i])p[tot++]=i;
        for (int j=0;j<tot && i*p[j]<=MAXN;j++) 
        {
            bz[i*p[j]]=0;
            if (i%p[j]==0)break;
        }
    }
}
————————————————
版权声明:本文为CSDN博主「路人黑的纸巾」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/enjoy_pascal/article/details/80372454

在这里插入图片描述

涨知识ing~~~
%%%%%%dalao 们

P3383 【模板】线性筛素数

做个水题
P3383 【模板】线性筛素数

在这里插入图片描述

裸题

AC code

  • 稍微注意一下0和1的特判
//Author:PhilFan;
#include<bits/stdc++.h>
#define MAXN 10000010
using namespace std;
int n,m,a[MAXN],p[MAXN],x;
void init(int n) 
{
	memset(p,0,sizeof(p));
	p[1]=1;  tot=0;
    for (int i = 2;i <= n;i++) 
    {
        if(a[i]==0)	p[tot++]= i;
        for (int j = 0;j < tot && i * p[j] <= n+5; j++) 
        {
            a[i * p[j]] = 1;
            if (i % p[j] == 0)	break;
        }
    }
}
	
int main()
{
	scanf("%d %d",&n,&m);
	init(n);
	a[0]=1,a[1]=1;
	for(int i = 1; i <= m; i++){
		scanf("%d",&x);
		if(a[x]==0){printf("Yes\n");}
		else{printf("No\n");}
		x=0;
	}	
	return 0;
}

P1865 A % B Problem网址

在这里插入图片描述

这道题是输出区间的质数,需要在线性筛法中加入一个前缀和的数组,因为不加的话会TLE一个点


//Author:PhilFan;
#include<bits/stdc++.h>
#define MAXN 10000010
using namespace std;
int n,m,a[MAXN],p[MAXN],b[MAXN],x,y,cnt;
void init(int n) 
{
    memset(p,0,sizeof(p));
    p[1]=1;  
    int tot=0;
    for (int i = 2;i <= n;i++) 
    {
        if(a[i]==0){
        	p[tot++]= i;
        	b[i]=b[i-1]+1;//前缀和数组   //如果有多的质数,数组++
		}
		else b[i]=b[i-1];//如果质数没有多,b[i]就和上一个一样
        for (int j = 0;j < tot && i * p[j] <= n+5; j++) 
        {
            a[i * p[j]] = 1;
            if (i % p[j] == 0)	break;
        }
    }
}
    
int main()
{
    scanf("%d %d",&m,&n);
    a[0]=1,a[1]=1;
    init(n);
    for(int i = 1; i <= m; i++){
        scanf("%d %d",&x,&y);
        if(x<1||x>n||y<1||y>n){		//特判
            printf("Crossing the line\n");
        }
        else{
			cout<<b[y]-b[x-1]<<endl;	//x有可能也是质数,所以是x-1
            x = 0 , y = 0;
        }
    }	
    return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值