数学技巧之素数筛选

如果只是对一个整数进行素性测试的只要o(√n)的复杂度便可以判定,蓝而如果是n个呢(n<=1000)照样可以,那如果100000个呢?对于普通的o(n√n)根本跑不动,因此我们必须寻找更加高效的算法,常用的筛选方法有埃氏筛法, 区间筛法,欧拉筛法。

1.埃氏筛法

首先,我们先把2-n范围内的数写下来,其中最小的素数是2,那么能被2整除的数便不是素数,那么我们可以把2的倍数都划去。然后剩下的最小素数便是3,我们便把3的倍数都划去,以此类推。这样反复操作我们就能枚举n以内的素数。

贴上伪代码:

1 const int N = 1000000 + 5;
2 int check[N], prime[N];
3 
4 int ptot = 0;
5 memset(check, 0, sizeof(check));
6 for(int i = 2; i <= n; i ++){
7     if(!check[i])prime[ptot++] = i;
8     for(int j = 2; i * j <= n; j ++) check[i * j] = 1;
9 }

 

但是问题又来了, 这个复杂度究竟该怎么算呢?怎么看像o(n2)呢?No No No~~

每次我们筛去每个数的整数倍像2筛了n/2个,3筛了n/3个;

因此总的复杂度约为:

哎呀!这不是发散级数吗?这个不是发散的数啊,那复杂度不是会爆掉吗?

别担心,虽然它是发散级数,但是他的增长速度非常慢,来我们测试一下

看到了吗?这个发散级数在n = 1000000的时候都没有超过14由此可见它的复杂度很小,近似等效成o(nloglogn),而对我们这些搞ACM的人来讲他大致看成线性的都无妨。

2.区间筛法

设区间[a,b)表示a<=x<b,的素数所构成的集合。

我们很容易知道b只要不能整除[2,√b]里面的素数,那么b就可以被认定为一个素数,因此,如果我们有√b里面的素数表,便可以结合埃氏筛法来判断。

附上伪代码:

 1 const int N = 100000 + 5;
 2 int check[N], prime[N];
 3 int is_prime[N];
 4 
 5 int ptot = 0;
 6 memset(check, 0, sizeof(check));
 7 memset(is_prime, 0, sizeof(is_prime));
 8 for(int i = 2; i <= n; i ++){
 9     if(!check[i])prime[ptot++] = i;
10     for(int j = 2; i * j <= sqrt(b); j ++) check[i * j] = 1;//筛[2,√b)
11     for(int j = a/i+1; i * j < b; j ++) is_prime[i * j - a] = 0;//筛[a,b)
12     //将[a, b)的数,移到[0, b - a)可节省空间
13 }

 

3.欧拉筛法

这便是重头戏,真真正正的o(n)的复杂度,不说什么,直接上代码。

 1 const int N = 1000000 + 5;
 2 int prime[N], check[N];
 3 
 4 memset(prime, 0, sizeof(prime));
 5 memset(check, 0, sizeof(check));
 6 int ptot = 0;
 7 for(int i = 2; i <= n; i ++){
 8     if(!check[i]) prime[ptot ++] = i;
 9     for(int j = 0; j < ptot; j ++){
10         if(prime[j] * i > n) break;
11         check[prime[j] * i] = 1;
12         if(i % prime[j] == 0) break;
13     }
14 }

 

这个貌似只是做了少许改进了吧,貌似就改进了第12行啊,聪明,蓝而就是因为这一行,导致了他的复杂度变成完美的线性, 设合数的最小质因数为p,则当它遍历到这个素数的时候因为第12行二跳出循环,这样直接导致了每个合数只能被它的最小质因数给删去。

下面给出证明:

设合数n的最小质因数为p,另一个比它大的质因数为p1,设 n = p1 * m1 = p * m,我们很容易就可以发现j循环到质因数p的时候合数n第一次被标记(若循环到p之前已经跳出循环则说明有比p更小的质因数),若也被p1标记,则是在这之前(因为p1 > p,所以m1 < m),考虑i循环到m1,注意到n = p*m=p1*m1且p,p1为不同的质数,因此p|m1,所以当j循环到质数p后就结束,不会循环到p1.这就说明了不会被p1筛去,说明合数只能被它最小的素数给删去。(很神奇吧!)

posted @ 2015-08-16 21:04  殇雪  阅读(3282)  评论(0编辑  收藏  举报