如何查找一个范围内的全部素数?ios
能够是从1~n挨个判断n%i 是否 == 0,也能够从 1~sqr(n) 一个个判断。数组
相信大家也据说过埃氏筛法,是使用每个数的倍数筛掉合数!可是!每个合数要被筛屡次!这就给了咱们优化的可乘之机!优化
它叫作线性筛,顾名思义,时间复杂度是线性的。blog
咱们都知道,线性的复杂度已经很是优秀了,接下来咱们的目的就是如何让每个合数只被筛一次。io
下面记住这条线性筛的原理:class
重要的事情说三遍!!!stream
#include<iostream> #include<cstdio> const int MaxN = 20025; bool notp[MaxN]; int prime[MaxN]; int tot; int main(){ int x; scanf("%d",&x); for(int i = 2; i <= x; i++){ if(notp[i] == 0){ prime[++tot] = i; } for(int j = 1; j <= tot; j++){ if(i * prime[j] > x) break; notp[i*prime[j]] = 1; if(i % prime[j] == 0) break; } } for(int i = 1; i <= tot; i++){ printf("%d ",prime[i]); } return 0; }
看起来很是简短,对不对?原理
下面解释一下这段代码:循环
输入x,求0~x区间中的全部素数。im
接下来,是2~x的枚举,为何从2开始相信各位也都明白。
notp数组的意思是"is not a prime",不是一个质数,值为0时表明它是一个质数。(在开始咱们已经把它默认设置为全为素数,尽管事实不是那样)
若是发现一个数"is not not a prime"(=="is a prime)就把它放到咱们的素数数组里,至于为何这么作稍后解答。(并且,咱们显然能够看出这个数组具备单调递增的性质)
接下来咱们就循环咱们的素数数组,对于每一个元素筛去它的质数倍。
相信 if ( i * prime[j] > x) break; 你们都理解,下面就是标记它是一个素数。
接下来就是重点!
线性筛所有的Knowledge都凝聚在这一句话!if ( i % prime[j] == 0 ) break;
它意味着,只有最小的质因数才能筛去其它的数。
由于,若是 i % prime[j] == 0 的话,这就是说 i 拥有一个最小的质因数 prime[j] ,缘由有两条:①咱们以前没有触发过 i % prime[j] == 0,那么就是说,以前的 prime[1~j]都不是i的任何因数。
② i % prime[j] == 0 意味着, i * prime[j] 等同于 prime[j] * i/prime[j] * prime[j] ,其中全部数均为整数,最小质因子就是 prime[j]。这违反了咱们在一开始的原理:
而咱们经过找规律发现,i * prime[j] 的最小质因子永远是素数序列中的prime[j]。
(例如,素数序列是 2,3,正在枚举4,4*2=8,最小质因子是2;素数序列是2,3,正在枚举3,2*3=6,3*3=9,2和3都是其中的最小质因子)
那么,接下来咱们要注意一个事情。为何 if( i % prime[j] == 0 ) break; 要放在 notp[i * prime[j]] = 1; 后面呢?难道不能放在它前面吗?
刚刚我解释过的缘由①,在触发 if( i % prime[j] == 0 ) break; 意味着prime[j]是i最早遇到的质因子,而且咱们知道某一个数的某个因子必定小于等于它自己,因此显而易见,咱们的素数序列包括到i(不管i是不是质数)的全部质数(若是i是质数,那么包括i)。咱们以前也说过这个质数序列是单调递增的,那么prime[j]就是 i 的最小质因子!
而咱们之因此在筛完 i * prime[j] 以后才运行这个条件判断,是由于 i % prime[j] 第一次 == 0 的时候,prime[j]是i的最小质因子!若是这里不break,那么下一次 i % prime[k] == 0的时候,显而易见,prime[k]不是i的最小质因子,这违反了咱们在开头给出的原理。为了不某些合数未来被筛第二次,因此咱们这里直接break,把筛掉剩下 i * prime[x] 的事情交给以后的质数序列去作。这样,就能够保证每一个质数只被筛一次。
最后输出质数序列,相信各位都明白。