zoukankan      html  css  js  c++  java
  • 线性筛法求素数

    题目:给出一个正整数n,打印出所有从1~n的素数(即质数);

    关键是要找出一个判断一个正整数n是否为素数的方法...


    傻瓜解法--n,n/2

     1 #include<stdio.h>
     2 int main()
     3 {
     4   int i,n;
     5   while(scanf("%d",&n)==1)
     6   { for(i=2;i<n;i++)
     7          if(n%i==0)    break; 
     8     if(i==n)    printf("YES
    ");
     9     else           printf("NO
    ");
    10   }
    11 }

    这是理所当然的想法,按照素数的定义,除了1和它本身没有其他的因数,就是素数。

    这种解法的缺点就是红色标注那里,i<n,或者有的是i<n....这种循环规模n稍微大点,运行时间不谈了,都是泪...


    普通解法--sqrt(n)

     1 #include<stdio.h>
     2 #include<math.h>
     3 int main()
     4 { int i,n,x;
     5   while(scanf("%d",&n)==1)
     6   { x=(int)sqrt(n);
     7     for(i=2;i<=x;i++)
     8          if(n%i==0)    break; 
     9     if(i>x)    printf("YES
    ");
    10     else           printf("NO
    ");
    11   }
    12 }

    这里循环取到sqrt(n),效率改进不少了...但显然还是不够理想....继续往下看


    普通筛选法--埃拉托斯特尼筛法

    先简单说一下原理:

    基本思想:素数的倍数一定不是素数
    实现方法:用一个长度为N+1的数组保存信息(0表示素数,1表示非素数),先假设所有的数都是素数(初始化为0),从第一个素数2开始,把2的倍数都标记为非素数(置为1),一直到大于N;然后进行下一趟,找到2后面的下一个素数3,进行同样的处理,直到最后,数组中依然为0的数即为素数。
    说明:整数1特殊处理即可。

    举个例子,N=20时,演示如下图:

    最后数组里面还是0的就是素数了...

    代码实现如下:

    prime[]用来保存得到的素数 prime[] = {2,3,5,7,11,.........} tot 是当前得到的素数的个数 check :0表示是素数  1表示合数

     1 memset(check, 0, sizeof(check));
     2 int tot = 0;
     3 for (int i = 2; i <= n; ++i)
     4 {
     5   if (!check[i])
     6   {
     7     prime[tot++] = i;
     8   }
     9   for (int j = i+i; j <= n; j += i)
    10   {
    11     check[j] = 1;
    12   }
    13 }

    此筛选法的时间复杂度是O(nloglogn) 空间复杂度是O(n)

    不足之处也比较明显,手动模拟一遍就会发现,很多数被处理了不止1遍,比如6,在素数为2的时候处理1次,为3时候又标记一次,因此又造成了比较大的不必要处理...那有没有改进的办法呢...就是下面改进之后的筛法...


    线性筛法--欧拉筛法

    先上代码吧...

     1 #include<cstdio>
     2 #include<cstring>
     3 #define MAXN 100005
     4 #define MAXL 1299710
     5 int prime[MAXN];
     6 int check[MAXL];
     7 
     8 int tot = 0;
     9 memset(check, 0, sizeof(check));
    10 for (int i = 2; i < MAXL; ++i)
    11 {
    12   if (!check[i])
    13   {
    14     prime[tot++] = i;
    15   }
    16   for (int j = 0; j < tot; ++j)
    17   {
    18     if (i * prime[j] > MAXL)
    19     {
    20       break;
    21     }
    22     check[i*prime[j]] = 1;
    23     if (i % prime[j] == 0)
    24     {
    25       break;
    26     }
    27   }
    28 }

    精华就在于红色标注那两处,它们保证每个合数只会被它的最小质因数筛去,因此每个数只会被标记一次,所以时间复杂度是O(n)

    还是按上面的例子进行一遍模拟:N=20

    此过程中保证了两点:

    1、合数一定被干掉了...

    2、每个数都没有被重复地删掉

    (证明 见参考资料)


    引申--求欧拉函数

    在数论,对正整数n,欧拉函数是小于或等于n的数中与n互质的数的数目。此函数以其首名研究者欧拉命名,它又称为Euler's totient function、φ函数、欧拉商数等。 例如φ(8)=4,因为1,3,5,7均和8互质。

    求欧拉函数的方法只需在上面的程序中稍有改动即可,此处只贴出代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #define MAXN 100005
     4 #define MAXL 1299710
     5 int prime[MAXN];
     6 int check[MAXL];
     7 int phi[MAXL];
     8 int tot = 0;
     9 phi[1] = 1;
    10 memset(check, 0, sizeof(check));
    11 for (int i = 2; i < MAXL; ++i)
    12 {
    13   if (!check[i])
    14   {
    15     prime[tot++] = i;
    16     phi[i] = i - 1;
    17   }
    18   for (int j = 0; j < tot; ++j)
    19   {
    20     if (i * prime[j] > MAXL)
    21     {
    22       break;
    23     }
    24     check[i*prime[j]] = 1;
    25     if (i % prime[j] == 0)
    26     {
    27       phi[i*prime[j]] = phi[i] * prime[j];
    28       break;
    29     }else
    30     {
    31       phi[i*prime[j]] = phi[i] * (prime[j]-1);
    32     }
    33   }
    34 }

    若是素数,那么从1~n-1都是和它互质的数,所以phi(i) = i - 1;

    另外两个是积性函数(见参考资料)的公式和欧拉函数的特性。


    参考资料

    1、http://suno.cnblogs.com/

    2、http://wenku.baidu.com/view/1187eebce009581b6ad9eb12

    3、http://blog.csdn.net/dinosoft/article/details/5829550

  • 相关阅读:
    Ambari 整体架构
    Ambari 介绍
    xcode工程命令行生成ipa安装包
    gradle打包java项目
    FreeMarker标签介绍
    P与NP,从概念到研究全面综述
    计算机领域经典笑话
    自己动手写GC
    编程语言简史
    不第后赋菊
  • 原文地址:https://www.cnblogs.com/grubbyskyer/p/3852421.html
Copyright © 2011-2022 走看看