数论 欧拉线性素数筛
简述
素数筛是数论的门票(签到工具)。
很多数论问题都需要用到素数筛。
本文将从作者自己的角度去阐述怎么写出欧拉线性筛。
素数筛的原理:
必定有一个 不小于所有质因数 ( ≤ ) 的质因数b 与 一个 大于等于所有因数 ( ≥ ) 的因数c ( c ≠ 1) 乘积等于 合数a
本文 最小 是 不小于等于 的意思
a = b * c ( a为合数,b 为 最小质因数 的质因数, c 为因数, 但 c 不等于 1 )
至于为什么?
因为
假设 a ≠ 1,b 是质因数,c是因数,但 c 不等于 1
a = b * c
那么,
c = d * e,b,c,e中有一个 最小质因数 的 质因数。
那么,
假设是 e.是最小质因数
a = ( b * d ) * e ,
因为
d ≥ e
那么 ( b * d ) 构成了一个≥ c 的因数
例如
12 = 3*4 = 3 * 2 * 2= 2*6 , 那么最小质因数就是2,6 是 > 3,2,1 的因数。
我们就需要 枚举这个 ≥ 每个因数 的数 去筛出合数,也就是确保每个合数被最小的质因数筛去
代码:
实现细节:
要 求出 【2,n】的质数,
1. 先从 小到大 枚举数 2~n 。
(为什么要枚举到n ? 因为 n 可能为质数)
( 为什么从小到大枚举? 为了找出筛去合数的最小质因数)
( 为什么不会筛掉质数? 如果是枚举的数是质数,因为质数 = 1 * 最小质因数, 我们从 2开始枚举,肯定不会筛到质数,
( 为什么可以保证,n以内的合数肯定被筛去.? 每个合数被最小质因数筛去,也就是每个合数 大于等于所有因数的因数 已经被枚举过, 到了n,就自然只有 n * 最小质因数 = Y 这个值没有被筛了,虽然Y可能为质数...... )
( 我还有问题!为什么不会合数不会被重复筛!!! 因为我们可以确保每个合数只被他的最小质因数筛去。确保外层枚举的因数 >= 每个因数 )
2. 然后用 因数 乘上 对应的最小质因数 去找出 要筛掉的合数。
3. 我们需要判断一下筛掉的 合数 的有没有超过 n 的范围。
4. 同时需要保证,我们是用最小质因数筛掉这个合数的( 确保外层枚举的因数 >= 每个因数 ),这样就避免了重复筛。
怎么保证?
因为外层循环枚举因数,要确保每个合数是被最小质因数筛去,那么就得 确保外层枚举的因数 >= 每个因数 。
a = b * c = d * e , c如果是最小质因数,那么 b ≥ d , e 。我们外层循环的 i 就得是 这个 b
例如:如果 i % prime[j] == 0 ,那么 i == k * prime[j] , 如果继续循环 j++ ,
下个要筛的合数就是 i * prime[j+1] == k * prime[j+1] * prime[j] ( prime[j+1] > prime[j] )
因为我们现在外层枚举的数是 i == k * prime[j] , 而当外层枚举的是 k * prime[j + 1] 才能去筛去。
不用继续筛后面的数了,跳出循环,继续枚举因数。
那么 可以码出。
const int MAXN = 10000000; int n,m; int prime[MAXN];//保存质数 bool istPrime[MAXN];//不是质数 int cnt; void sPrime(int n){ istPrime[1] = true; for(int i = 2;i <= n; ++i){ //枚举大于与等于每个因数的因数 if(!istPrime[i]) prime[cnt++] = i; for(int j=0;j<cnt && i*prime[j] <= n;j++){ //枚举最小质因数 istPrime[i*prime[j]] = true; //筛掉合数 if(i%prime[j] == 0){ // 确保外层枚举的因数 >= 每个因数 break; } } } }
推荐题目: