今天genius刚刚学习了线性筛素数,当然对于线性的东西,本人还是不太精通。
但对于筛素数,还是有一定的认识了;
筛素数,说白了,就是在一堆有序数中找质数-_-||。
我们需要求解的,是一个线性表(数组p[ ]),使得我们可以输入一个数,直接从数组中查询它是否是素数(true或false),并且每次查询的速度均为O(1)。
那么为了在更快的时间内构建这张表,我们需要一种O(n)的算法,使得每一个数只判断一次,就能知道它是否是素数。这就是我们的需求:
1. 每个数都被判断到
2. 每个数只被判断一次
根据唯一分解定理,每一个合数都可以被分解成若干个质因子的乘积。那么,如果我们已经找到了一个数是质数,那么它的倍数就一定不是质数。这就是平时我们所接触到的判断素数的方法,它的复杂度为O(nloglogn)" data-mce-tabindex="0">O(nloglogn)O(nloglogn),在大多数时候,已经够好了,但离我们今天的目标还有些距离。 我们依旧从这个角度出发,用一个bool数组 x[n]来表示这个数是否为质数,用一个数组p[n]来存储每一个素数。那么伪代码就可以写出来了:
1 for(1~n){ //从1到n判断是否为质数 2 if(x[i]==true) i加入p[ ]; //为素数 3 for(遍历所有素数) 4 x[该数与素数的乘积]=false; 5 }
欧拉筛法
“一个比‘合数的最小质因数’更大的质因数与该合数的乘积等于一个更大的合数与更小的质数的乘积”。
这句话说起来可能有点绕口。我们还是上公式吧:
根据唯一分解定理,一个合数x=p1*p2 * p3….其中p为质数,且p1为最小的质因数。那么,x与p2的乘积=(p1 p2 *p3…. ) *p2= p1 (p2 p2 p3….)
显然,p1< p2,左式的括号小于右式的括号。
那么这就有一种很奇妙的筛法:
将第二个for中的“遍历所有素数”改为“从2到该数最小的质因数”。(比如15就是2,3;20就是2;)
这样做的道理何在?其实就是刚刚我们所讲那东西的原理:如果我们将x与p2相乘,得到的这个数会在(x/p1)*p2与p1相乘的时候再被判断一次!这就是重复的原因所在。所以,我们将其改为到最小质因数停止时,我们就可以保证这个数不被再判断了!(如15 *2=30,15 *3=45,30只会在15的时候被判断,45只会在15时被判断)
那么我们就可以得出这个结论了:每个数都被判断且仅判断过一次!
证毕!
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; bool is[10005]; int p[10005]; int num=0; int main() { int n; scanf("%d",&n); memset(is,true,sizeof(is)); for(int i=2;i<=n;i++) { if(is[i]) { num++;p[num]=i; } for(int j=1;j<=num&&i*p[j]<=n;j++) { is[i*p[j]]=false; if(i%p[j]==0)break; } } for(int i=1;i<=num;i++) printf("%d ",p[i]); return 0; }