求质数比较高效的算法——线性筛质数(埃拉托色尼筛选法的优化)
-
基本原理
每次用该合数的最小质因子消除这个合数,所以就不会重复消除,也能保证将合数全部消除掉。同时质数本身没有最小质因子,所以质数不会被消除掉。
总共设置两个数组
-
int primes[N]:从下标0开始,记录2到n的质数。
-
bool st[N]:标志这个下标(从0开始)对应的数字是否是数字,为0代表是质数,为1代表是合数。这个数组要初始化全为0。
设置一个cnt变量,初值为0,记录执行到目前质数的个数。
-
-
外循环i=2到n(n表示要求的范围),首先判断st数组,对应的i这个数是否是质数,是的话则将其加入到primes数组。
-
而后就是利用 i 和目前已求解到的所有质数来消除部分合数,从最小质数开始遍历,保证 j<cnt,且 primes[j] x i < max,那么如何保证这个质数是该合数的最小质因子呢?首先从2开始,2毋庸置疑是拥有2这个质因子的合数的最小质因子,如果往后执行,到3呢?其实我们只要知道 i 和当前质因子 primes[j] 的关系,如果 i % primes[j] =0, 他们的商为 m 。说明什么?说明你下一次消除的合数 i x primes[j+1] 完全可以等同于 k x primes[j] ,k为primes[j+1] x m ,说明下一次消除合数所用的质因子并非最小,所以需要break,跳出判断合数这个循环。
-
代码
#include <iostream> #include <algorithm> #include<cstdio> using namespace std; const int N = 1000010; int primes[N], cnt; //题目本意是求素数个数,当然也能求每个数的最小质因子 int st[N]; void get_primes(int n) { for(int i = 2; i <= n; i++) { if(!st[i]) primes[cnt++] = i; // j<cnt,防止越界到没有质数的位置, primes[j]*i <= n 防止消除的合数超过了最大值。 for(int j = 0; j<cnt && primes[j]*i <= n; j++) { // 从已有的质数中,筛除合数,用合数的最小质因子筛除这个合数 st[primes[j]*i] = 1; // 防止被重复消除,若此时i的值是某一个质数的倍数,相乘消除一个合数之后,就要break了! // 为什么呢?因为i是primes[j]的几倍,那么就不能再用i*primes[j+1]消除这个合数了。 // 因为primes[j+1]不是最小的质因数,而primes[j]才是最小的,因为i本身都是这个合数的因数,那么primes[j]也是这个合数的因素,明显primes[j]更小。 if(i % primes[j] == 0) break; } } } int main() { int n; cin >> n; get_primes(n); cout << cnt << endl; return 0; }
- 求解某一范围内的质数,以及判断该质数是否为质数,都可以用这个算法。可以用预处理某一个大范围内,求解出质数的个数,或者用标记数组标记每个质数的位置。
- 同时判断不是质数有一个快的方法,就是你要判断的这个数字n,(n+1)%6!=0 && (n-1)%6!=0,那么这个数字一定不是质数,大于3的质数,都是分布在6的倍数这个数字的左边和右边,比如5,7、分布在6的左右边。
- 但是反之不一定,分布在6的倍数这个数字的左右边也不一定是质数,比如35,分布在36的左边,但并不是质数。