深入理解线性质数(素数)筛

预备知识
首先不管是什么方法的筛质数,其最基本的思想都是一样的:假设P是任意一个数,如果这个数没有被2~P-1之间的任何数筛掉,那么它就是质数
算术基本定理可表述为:任何一个大于1的自然数 N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积(取自百度)

int p[N], cnt; //p[N]用来存质数,cnt表示质数的个数
bool st[N]; //表示当前的数有没有被筛过
朴素筛法 O ( n l o g n ) O(nlogn) O(nlogn)
void get_primes(int n)
{
	for(int i = 2; i <= n; i ++)
	//对于每一个i,从i~n中找到i的每一个倍数并筛掉
		if(!st[i]) p[cnt ++] = i; // 如果没有被筛掉,那么就是质数
		for(int j = i + i; j <= n; j += i)
			st[j] = true;
}

由于当运行到 i 时可以确保已经用小于 i 的每一个质数都筛过一遍,因此可以保证每一个合数都会被筛掉

埃式筛法 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)

在朴素筛法中,2 * 4 与 4 * 2 的结果都是8,因此一个数可以用合数筛的时候,其实已经被质数已经筛过一次,所以可以只在 i 是素数的时候筛

void get_primes(int n)
{
	for(int i = 2; i <= n; i ++)
		if(!st[i])
		{
			p[cnt ++] = i;
			for(int j = i + i; j <= n; j += i)
				st[j] = true;
		}
}

由算术基本定理知一定可以将每一个合数都筛掉(因为每一个合数都能分成若干个质数)

线性筛法 O ( n ) O(n) O(n), n = 1e7时基本上就比埃式筛法快一倍

算法核心:保证每个合数只会被其最小质因子筛掉

void get_primes(int n)
{
	for(int i = 2; i <= n; i ++)
		if(!st[i]) p[cnt ++] = i;
		for(int j = 0; p[j] * i <= n; j ++)
		{
			st[p[j] * i] = true;
			if(i % p[j] == 0) break;
		}
}

首先证明合数是不是一定会被筛掉
假设N是一个合数,且P是N的最小质因子
故 有 N = P ∗ N P 故有N = P * \frac{N}{P} N=PPN
只需要满足P小于 N P \frac{N}{P} PN,当 i 等于 N P \frac{N}{P} PN时,N就会被筛掉
反证法,假设 N P \frac{N}{P} PN小于P
如果 N P \frac{N}{P} PN是质数,那么 N P \frac{N}{P} PN就是N的最小质因子,故 N P \frac{N}{P} PN小于P不成立
如果 N P \frac{N}{P} PN是合数,那么由算术基本定理, N P \frac{N}{P} PN一定可以分出一个比P小的素数来作为N的最小质因子,故 N P \frac{N}{P} PN小于P不成立
综上,P一定小于 N P \frac{N}{P} PN,故每一个合数肯定会被筛掉

其次证明是不是每个合数只会被其最小质因子筛掉

for(int j = 0; p[j] * i <= n; j ++)
{
	st[p[j] * i] = true;
	if(i % p[j] == 0) break;
}

如果 i % p[ j ] == 0 说明p[ j ]是 i 的最小质因子(因为p[ j ]是从小到大枚举的), 所以p[ j ]也是 i * p[ j ]的最小质因子
如果 i % p[ j ] != 0 说明p[ j ]小于 i 的所有质因子(因为p[ j ]是从小到大枚举的),所以p[ j ]也是 i * p[ j ]的最小质因子
而至于为什么要break,因为一旦 i % p[ j ] 为0时,说明p[ j ]是 i 的最小质因子,而此时如果继续循环下去,用p[ j + 1 ] * i 筛时,由于 i 的最小质因子p[ j ]小于p[ j + 1],因此这时候被筛掉的合数就不是被他的最小质因子筛掉的了
所以break也是保证线性筛法时间复杂度的一个重要原因~

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值