数论之质数筛法总结

                 数论之质数筛法

  基本定义及性质:

  数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数。

    在整个自然数集合中,质数的数量不多,分布比较稀疏,对于一个足够大的整数N,不超过N的质数大约有 N/lnN 个,即每lnN个数中有一个质数。

    质数的判断:

   有一个简单而又暴力的算法,即试除法, 即扫描 2~ sqrt(N) 之间的所有整数,依次检查它们能否整除N。

    伪代码如下:

1 bool is_prime(int n)
2 {
3     if(n < 2) return false;
4     for(int i = 2;i <= sqrt(n);i++)
5         if(n%i == 0) return false;
6     return true;
7 }

   时间复杂度为: O(sqrt(N))。

    有一些效率更高的随机性算法,不过水平超出了我们的范畴,不再讨论。

   

    质数的筛选:

   质数的筛法有许多种,先来介绍一个最实惠常用的埃拉托色尼斯筛法

    我们可以从2开始, 由小到大扫描每个数x,它的倍数 2x,3x,....,[N/x] * x 标记为合数。

    当扫描到一个数时,若它尚未被标记,则它不能被2~x - 1之间的任何数整除,该数就是质数。

    伪代码如下:

 1 void primes(int n)
 2 {
 3     memset(vis, 0, sizeof(vis));
 4     for(int i = 2;i <= n;i++)
 5     {
 6         if(vis[i]) continue;
 7         printf("%d\n", i); //i是质数
 8         for(int j = i;j <= n/i;j++) vis[i*j] = 1; 
 9     }
10 }

   时间复杂度为:O(NloglogN);

  该算法实现简单,效率已经非常接近于线性,是算法竞赛中最常用的质数筛法。

  

  再来介绍一种更优的算法:线性筛(欧拉筛法)

  算法思想:通过"从大到小累积质因子"的方式标记每个合数,设数组 v 记录每个数的最小质因子,我们按以下步骤维护 v;

    1.依次考虑 2~N 之间的每一个 i.

    2.若v[i] = i, 说明 i 是质数, 把它保存下来。

    3.扫描不大于 v[i] 的每个质数, 令 v[i*p] = p. 也就是在 i 的基础上累积一个质因子 p,因为 p <= v[i],所以p就是合数 i*p 的最小质因子。

  伪代码如下:

 1 void primes(int n)
 2 {
 3     memset(v, 0, sizeof(v));//最小质因子 
 4     int m = 0;//质数数量
 5     for(int i = 2;i <= n;i++)
 6     {
 7         if(v[i] == 0)
 8         {
 9             v[i] = i;
10             prime[++m] = i;//i是质数 
11         }
12         //给当前的数i乘上一个质因子
13         for(int j = 1;j <= m;j++)
14         {
15             // i有比prime[j]更小的质因子,或者超出n的范围,停止循环。
16             if(prime[j] > v[i] !! prime[j] > n/i) break;
17             //prime[j]是合数i*prime[j]的最小质因子
18             v[i*prime[j]] = prime[j]; 
19         } 
20     } 
21 }

  其实还可以利用欧拉筛法顺带求出欧拉函数,这里先提供伪代码。

     伪代码如下:

 1 void eular(int n)
 2 {
 3     //数组phi 表示欧拉函数 
 4     int cnt = 0;
 5     for(int i = 2;i <= n;i++)
 6     {
 7         if(v[i] == 0)
 8         {
 9             prime[++cnt] = i;phi[i] = i-1;
10         }
11         for(int j = 1;j <= cnt;j++)
12         {
13             if(prime[j]*i > n) break;
14             v[prime[j]*i] = 1;
15             if(i%prime[j] == 0)
16             {
17                 phi[i*prime[j]] = phi[i]*prime[j];
18                 break;
19             }
20             else
21                 phi[i*prime[j]] = phi[i]*(prime[j]-1);
22         }
23     }
24 }

猜你喜欢

转载自www.cnblogs.com/smilke/p/10914146.html