• 数论之质数筛法总结


                     数论之质数筛法

      基本定义及性质:

      数又称素数。一个大于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
    ", 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 }
  • 相关阅读:
    c#声明数组
    【游戏物理】欧拉、龙格、韦尔莱
    当const放在function声明后
    【物理】AABB物理碰撞检测
    100 Path Sum
    Loop Unrolling 循环展开
    Unity Shader and Effects Cookbook问题记录
    【ShaderToy】画一个球体
    pymysql
    mysql表间的关系和查询
  • 原文地址:https://www.cnblogs.com/smilke/p/10914146.html
Copyright © 2020-2023  润新知