把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

线性筛

前言

线性筛是筛素数一种比较常用的方法(实际上,它的用途含有很多,如筛\(\mu,\phi\)等玄学的函数)。它的时间复杂度近似于\(O(n)\)

代码(代码后面有解析)

#include<bits/stdc++.h>
using namespace std;
int n;
map<int,int> Is_Prime;//保存每一个数是否为质数
vector<int> Prime;//保存全部质数
int main()
{
	scanf("%d",&n);//筛选2~n内的质数
	for(int i=2;i<=n;i++) Is_Prime[i]=1;//先默认全部都是质数
	for(int i=2;i<=n;i++)
	{
		if(Is_Prime[i]) Prime.push_back(i);//如果这个数是质数,就将其保存下来
		for(int j=0;j<Prime.size();j++)//将这个数与已有的质数进行操作
		{
			Is_Prime[i*Prime[j]]=0;//将这个数与该质数的积标记为非质数
			if(!(i%Prime[j])) break;//如果当前数是该质数的倍数,就退出循环(这个在后面会进行解释)
		}
	}
	printf("%d\n",Prime.size());
	for(int i=0;i<Prime.size();i++) printf("%d ",Prime[i]);
	return 0;
}

解释

对于\(2\sim n\)之间的一个整数\(i\),有两种可能性:

一:它是质数。则此时\(Prime[j]\)必为\(i\)(一个质数只能被\(1\)和本身整除,而\(Prime[j]\)不可能为\(1\),故\(Prime[j]\)\(i\)),而\(i\)是刚保存下来的,必然在最末尾,即i后面没有其他可操作的数了,于是退出循环

二:它是合数。则可得\(i=Prime[j]*x\)\(x\)\(2\sim i\)之间的一个整数),此时又有两种可能性:

1:\(x\)是质数。则\(x\ge Prime[j]\)(若\(x<Prime[j]\),则在对\(Prime[j]\)进行操作前就会对\(x\)进行操作并退出循环),那么\(i\)\(Prime[j]\)之后的任意一个数\(t\)的积\(i*t\)都可以表示为\(Prime[j]*x*t\),而\(x*t\)是一个合数,所以在轮到对\(x*t\)\(Prime[j]\)进行操作时时就可以得到\(i*t\),故不需要继续操作了,可以退出循环

2:\(x\)是合数。则\(x\)可以分解为\(a*b\)的形式(其中\(a\)是所能取值的数中的最小质数),那么\(i=a*b*Prime[j]\)。对于\(a\)\(Prime[j]\)的大小关系,又有两种可能性:

​ ①:\(a<Prime[j]\)。显然不可能(否则在对\(Prime[j]\)进行操作前就会对a进行操作并退出循环)

​ ②:\(a\ge Prime[j]\)\(∵a\ge Prime[j],∴b*Prime[j]\le x\),那么\(i\)应在对\(x\)进行操作就已经在对\(b*Prime[j]\)进行操作的时候的时候被判定为非质数,自然无需再判一遍。

posted @ 2018-10-29 18:35  TheLostWeak  阅读(551)  评论(0编辑  收藏  举报