素数筛

最基础的找素数的方式是从 \(2\)\(x-1\) 全除一遍,后来变成除到 \(\sqrt{x}\) 就行辽(因为因数是成对出现的)。

既然判一个数要 \(O(n)\) 不如一边把小于 \(n\) 的数全判了。

就出现了筛法。

首先是埃筛(埃拉特斯托尼筛法)由于 素数唯一分解定理 ,我们只要记录下到目前数为止的质数,再把目前数的质数倍数标记为非素数。然而这还不是线性。一个数会筛其质因子个数次。


上面没什么用的,直接从这里开始也行。

最终出现了线性筛。

大体和埃筛相同,但是在标记非负数时,一旦目前数可以被记录的质数整除就跳到下一个质数。意思就是只会标记一个数小于最小质因子的质数倍数。

关于准确性:

\[\bf 对于一个数 \;\mit x\;\bf可表示为\;\mit s\times p_{min}\;\bf 即最小质因子的整数倍。\\ 如果我们要筛\;\mit s\times p_{min}\times p_x\quad (p_x>p_{min})\;\bf 就会和 \;\mit s\times p_{x}\times p_{min}\;\bf 筛重。 \]

其实就是 \(p\) 的乘的顺序不同但是都会被筛上,只要我们给其定一个排列规律,即为只会乘比现在最小质因子小的质数,就保证了 \(p\) 是从大到小排列的,一个序列只会出现一次。

这样所有的数就会且只会筛一次。

\(\frak code\)

#include<cstdio>
#include<algorithm>
using namespace std;
typedef int int_;
#define int long long


int n,tot;
int inp[3000050];
int prime[3000050];


int_ main()
{
	scanf("%lld",&n);
	inp[1]=inp[0]=1;
	for(int i=2;i<=n;i++){
		if(!inp[i]){
			prime[++tot]=i;
		}
		for(int j=1;j<=tot && prime[j]*i<=n;j++){
			int tp=prime[j]*i;
			inp[tp]=1;
			if(i%prime[j] == 0) break;
		}
	}
	for(int i=1;i<=tot;i++){
		printf("%lld ",prime[i]);
	}
	return 0;
}

-EOF-
posted @ 2019-11-23 17:58  T_horn  阅读(260)  评论(0编辑  收藏  举报