素数判定 -- 埃筛,欧拉筛

素数(质数):质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数

初学者的素数判定
  • 判断一个数是否是素数
  • 时间复杂度:O( sqrt(n) )
bool is_prime(int n)
{
	if(n==1) return false;		//1不是素数,2是素数
	
	for(int i=2;i*i<=n;i++)		//判断 2~sqrt(n)是否有n的因数
	{
		if(n%i==0)
			return false;
	}
	return true;
}

素数筛

  • 判定1~n中有哪些是素数
1.埃筛
(1)最暴力的筛法:从2开始一直筛到n,每次筛掉i的倍数
  • 因为对于i: 2~n ,i 的倍数一定不是素数

    例如3是素数,但3的倍数 6,9,12…3n,一定不是素数

  • 时间复杂度: O(nlog n)

const int N=1000;
int isp[N];   //1代表不是素数,0代表是
void is_prime(int n)
{
    isp[1]=1;		//1不是素数
	for(int i=2;i<=n;i++)
	{
		for(int j=i*2;j<=n;j+=i)
			isp[j]=1;
	}
}
(2)埃筛的改进:
  1. 已被筛过的数不需要再筛
  2. 内层循环从i*i开始
  3. 时间复杂度 O(nlog log n)
const int N=1000;
int isp[N];   //1代表不是素数,0代表是
void is_prime(int n)
{
    isp[1]=1;			//1不是素数
	for(int i=2;i<=n;i++)
	{
	    if( isp[j]) continue;
		for(int j=i*i;j<=n;j+=i)
			isp[j]=1;
	}
}

埃筛的缺陷:用埃筛,有些数被筛了很多次
例如 30=2*15,在2的时候被筛掉,30=5 *6,在5的时候又被筛掉。
那么如何确保每个合数只被筛选一次呢?我们只要用它的最小质因子来筛选即可,这便是欧拉筛法。

2.欧拉筛(线性筛)
  • 欧拉筛法的基本思想 :在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。每个数只筛一次。
  • 时间复杂度:O(n)
const int N=1000;
int isp[N],pri[N],cnt=0;
//isp:判断是否是素数, pri:记录筛出来的质数, cnt:计数
void is_prime(int n)
{
    isp[1]=1;						//1不是素数
	for(int i=2;i<=n;i++)
	{
	    if(!isp[i]) pri[cnt++]=i;	//记录素数
		for(int j=0;j<cnt;j++)
		{
			if(i*pri[j]>n) break;
			isp[i*pri[j]] =1;		//pri[j]相当于最小质因子,筛掉以pri[j]为最小质因子的数
			if(i%pri[j]==0) break;		
		}
	}
}

(此处运用某位大佬的解释)
对于 i%pre[j] == 0 就break的解释 :当 i是pri[j]的倍数时,i = k*pri[j].
如果继续运算 j+1,i * pri[j+1] = k * pri[j] * pri[j+1],这里pri[j]是最小的素因子,当i = k * prime[j+1]时会重复,所以才跳出循环。
举个例子 :i = 8 ,j = 1,prime[j] = 2,如果不跳出循环,prime[j+1] = 3,
8 * 3 = 2 * 4 * 3 = 2 * 12,在i = 12时会计算。因为欧拉筛法的原理便是通过最小素因子来消除。

程序理解如图:
在这里插入图片描述

推荐博客:
link 埃筛与欧拉筛

推荐题目:
link 洛谷欧拉筛模板题

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值