• 求素数大有学问


    最近看了几道历年来找工作的笔试题目,很有几道是和素数相关的,本来也没有怎么上心,就觉得求素数么,不就弄个for循环,判断到当前要判断的数的开方即可,可是linFen的博客让我看的是一愣一愣的,所以在此做个笔记。

    定理1:如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d。则有代码如下:

     1 //版本1
     2 //定理:如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
     3 int isPrime(unsigned long long n)
     4 {
     5     if (n<2)
     6     {
     7         return 0;
     8     }
     9     if (n==2)
    10     {
    11         return 1;
    12     }
    13     for (unsigned long long i=3;i*i<=n;i++)
    14     {
    15         if(n%i==0) {
    16             return 0;
    17         }
    18     }
    19     return 1;
    20 }

    然后改进,去掉大于2的偶数的判断,可以节省一半的时间:

     1 //版本2,去掉偶数的判断
     2 int isPrime_without_even(unsigned long long n)
     3 {
     4     if (n<2)
     5     {
     6         return 0;
     7     }
     8     if (n==2)
     9     {
    10         return 1;
    11     }
    12     if (n%2==0)
    13     {
    14         return 0;
    15     }
    16     for (unsigned long long i=3;i*i<=n;i+=2)
    17     {
    18         if(n%i==0) {
    19             return 0;
    20         }
    21     }
    22     return 1;
    23 }

    然后还有一个关于素数的定理如下:

    定理: 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个"素数"因子d.
    证明:

    I1. 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
    I2. 如果d是素数, 则定理得证, 算法终止.
    I3. 令n=d, 并转到步骤I1.

    上述过程需要我们知道已有的素数的集合,则计算素数和改进的版本如下:

     1 //版本3方法补充
     2 //此方法甚好,可是有一点,需要构造一个素数保存序列,下列是构造素数序列函数
     3 //求前n个素数的素数序列。
     4 void makePrimes(unsigned long long primes[],unsigned int n)
     5 {
     6 //    FILE* zxh=fopen("zxh.txt","w");
     7 //    fprintf(zxh,"{2,3");
     8     primes[0]=2;
     9     primes[1]=3;
    10     int flag=true;
    11     unsigned long long i=5;
    12     unsigned int pointer=2,j;
    13     for(;pointer<n;i+=2)
    14     {
    15         flag=true;
    16         for (j=0;primes[j]*primes[j]<=i;j++)
    17         {
    18             if (i%primes[j]==0)
    19             {
    20                 flag=false;
    21             }
    22         }
    23         if (flag==true)
    24         {
    25             primes[pointer++]=i;
    26 //            fprintf(zxh,",%lld",i);
    27         }
    28     }
    29 //    fprintf(zxh,"}");
    30 //    fclose(zxh);
    31 }
    32 
    33 //版本3
    34 int isPrime_with_primes(unsigned long long primes[],unsigned long long n)
    35 {
    36     if (n<2)
    37     {
    38         return 0;
    39     }
    40     for (unsigned long long i=0;primes[i]*primes[i]<=n;i++)
    41     {
    42         if (n%primes[i]==0)
    43         {
    44             return 0;
    45         }
    46     }
    47     return 1;
    48 }

    其实如果我们经常求做对素数的操作的时候,我们还可以把以前生成的素数都保存下来,以便于以后使用。正如上面makePrimes函数中我注释掉的,我们可以把计算好的素数保存下来到一个文件中,以后每次使用的时候只需要拷贝即可。

    根据素数定理

    x/(lnx-0.5)<π(x)<x/(lnx-1.5),其中π(x)表示小于x的素数的个数。

    左边不等式对于x>=67成立,右边不等式对于x>√e3≈4.48169...成立.

      

    则我们可以事先算好小于unsigned long long内的所有的素数即可。

    假设我们要判断unsigned long long (2^64)以内的素数,我们就需要2^32内的素数即可判断,即有:

    Max unsigned long (MUL)= 2^32=4294967296

    π(MUL)<MUL/(ln(MUL)-1.5)=207679878.59807090275597192690663≈207679879

    即我们只要事先计算好207679879个素数的就可以很容易的判断2^64范围之内的所有的数是否为素数。

    假设我们已经有计算好的素数的数组Primes,则当小于我们已经计算好的素数的最大值时候,只需要在素数数组中采用二分查找即可,当大于的时候需要上述方法来判断。代码如下:

     1 //  二分查找,看数组Primes里面是否存在一个elem
     2 //  若存在,返回索引值
     3 //  若不存在,返回-1
     4 int binary_search_prime(unsigned long long Primes[],unsigned long long elem,unsigned int low,unsigned int high)
     5 {
     6     if (low>PRIME_NUM-1||high<low||high>PRIME_NUM-1)
     7     {
     8         return 0;
     9     }
    10     int mid=0;
    11     while(low<=high){
    12         mid=(low+high)/2;
    13         if (elem==Primes[mid])
    14         {
    15             return mid;
    16         }
    17         else if(elem<Primes[mid])
    18         {
    19             high=mid-1;
    20         }
    21         else
    22         {
    23             low=mid+1;
    24         }
    25     }
    26     return -1;
    27 }
    28 
    29 //  总体来讲判断一个64位数是否是素数,
    30 //  当小于我们已经计算好的素数的最大值时候,只需要在素数数组中查找即可,当大于的时候需要上述方法来判断。
    31 //  假设我们所求的素数是在已经缓存素数的范围内,则在判断是否是素数的时候只需要二分查找即可
    32 int is_prime_with_precompute(unsigned long long Primes[],unsigned long long elem)
    33 {
    34     if (elem<=Primes[PRIME_NUM-1])
    35     {
    36         return (-1!=binary_search_prime(Primes,elem,0,PRIME_NUM-1));
    37     }
    38     else
    39     {
    40         unsigned int j=0;
    41         for (j=0;Primes[j]*Primes[j]<=elem;j++)
    42         {
    43             if (elem%Primes[j]==0)
    44             {
    45                 return 0;
    46             }
    47         }
    48     }
    49     return 1;
    50 }

    上面的这种方法已经省去了很多的时间,可以大大帮助判断64位整数是否是素数,但是根据素数定理

    x/(lnx-0.5)<π(x)<x/(lnx-1.5),其中π(x)表示小于x的素数的个数。

    左边不等式对于x>=67成立,右边不等式对于x>√e3≈4.48169...成立.

    也就是说,我们已知了x之后,我们就可以判断小于x的素数大概有多少个,则我们在查找素数表的时候就不用在[0,PRIME_NUM-1]范围内查找了,新的查找范围是[x/(lnx-0.5)-1,x/(lnx-1.5)],则is_prime_with_precompute算法可以改写如下:

     1 int is_prime_with_precompute_optimize(unsigned long long Primes[],unsigned long long elem)
     2 {
     3     if (elem>67&&elem<=Primes[PRIME_NUM-1])
     4     {
     5         unsigned int 
     6         low=(int)(elem/(log((double)elem)-0.5))-1,
     7         high=(int)(elem/(log((double)elem)-1.5));
     8 
     9         return (-1!=binary_search_prime(Primes,elem,low,high));
    10     }
    11     else
    12     {
    13         unsigned int j=0;
    14         for (j=0;Primes[j]*Primes[j]<=elem;j++)
    15         {
    16             if (elem%Primes[j]==0)
    17             {
    18                 return 0;
    19             }
    20         }
    21     }
    22     return 1;
    23 }

    好了,大功告成,全部的代码奉上,希望各位多拍砖头。

      1 #include <math.h>
      2 #define PRIME_NUM 2076
      3 
      4 //版本1
      5 //定理:如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
      6 int isPrime(unsigned long long n)
      7 {
      8     if (n<2)
      9     {
     10         return 0;
     11     }
     12     if (n==2)
     13     {
     14         return 1;
     15     }
     16     for (unsigned long long i=3;i*i<=n;i++)
     17     {
     18         if(n%i==0) {
     19             return 0;
     20         }
     21     }
     22     return 1;
     23 }
     24 //版本2,去掉偶数的判断
     25 int isPrime_without_even(unsigned long long n)
     26 {
     27     if (n<2)
     28     {
     29         return 0;
     30     }
     31     if (n==2)
     32     {
     33         return 1;
     34     }
     35     if (n%2==0)
     36     {
     37         return 0;
     38     }
     39     for (unsigned long long i=3;i*i<=n;i+=2)
     40     {
     41         if(n%i==0) {
     42             return 0;
     43         }
     44     }
     45     return 1;
     46 }
     47 
     48 //版本3方法补充
     49 //此方法甚好,可是有一点,需要构造一个素数保存序列,下列是构造素数序列函数
     50 //求前n个素数的素数序列。
     51 void makePrimes(unsigned long long primes[],unsigned int n)
     52 {
     53     FILE* zxh=fopen("zxh.txt","w");
     54     fprintf(zxh,"{2,3");
     55     primes[0]=2;
     56     primes[1]=3;
     57     int flag=true;
     58     unsigned long long i=5;
     59     unsigned int pointer=2,j;
     60     for(;pointer<n;i+=2)
     61     {
     62         flag=true;
     63         for (j=0;primes[j]*primes[j]<=i;j++)
     64         {
     65             if (i%primes[j]==0)
     66             {
     67                 flag=false;
     68             }
     69         }
     70         if (flag==true)
     71         {
     72             primes[pointer++]=i;
     73             fprintf(zxh,",%lld",i);
     74         }
     75     }
     76     fprintf(zxh,"}");
     77     fclose(zxh);
     78 }
     79 
     80 //版本3
     81 //定理: 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个"素数"因子d.
     82 //证明: I1. 如果n不是素数, 则n有满足1<d<=sqrt(n)的一个因子d.
     83 //        I2. 如果d是素数, 则定理得证, 算法终止.
     84 //        I3. 令n=d, 并转到步骤I1.
     85 int isPrime_with_primes(unsigned long long primes[],unsigned long long n)
     86 {
     87     if (n<2)
     88     {
     89         return 0;
     90     }
     91     for (unsigned long long i=0;primes[i]*primes[i]<=n;i++)
     92     {
     93         if (n%primes[i]==0)
     94         {
     95             return 0;
     96         }
     97     }
     98     return 1;
     99 }
    100 
    101 //  根据素数定理:
    102 //  这个公式的新近改进如下:
    103 //  x/(lnx-0.5)<π(x)<x/(lnx-1.5)
    104 //    左边不等式对于x>=67成立,右边不等式对于x>√e3≈4.48169...成立.
    105 //  
    106 //  则我们可以事先算好小于unsigned long long内的所有的素数即可。
    107 //  假设我们要判断unsigned long long 2^64以内的素数,我们就需要2^32内的素数即可判断,即有:
    108 
    109 //  Max unsigned long (MUL)=2^32=4294967296
    110 //  π(MUL)<MUL/(ln(MUL)-1.5)=207679878.59807090275597192690663≈207679879
    111 //  即我们只要事先计算好207679879个素数的话 就可以很容易的判断2^64范围之内的所有素数
    112 
    113 
    114 //  二分查找,看数组Primes里面是否存在一个elem
    115 //  若存在,返回索引值
    116 //  若不存在,返回-1
    117 int binary_search_prime(unsigned long long Primes[],unsigned long long elem,unsigned int low,unsigned int high)
    118 {
    119     if (low>PRIME_NUM-1||high<low||high>PRIME_NUM-1)
    120     {
    121         return 0;
    122     }
    123     int mid=0;
    124     while(low<=high){
    125         mid=(low+high)/2;
    126         if (elem==Primes[mid])
    127         {
    128             return mid;
    129         }
    130         else if(elem<Primes[mid])
    131         {
    132             high=mid-1;
    133         }
    134         else
    135         {
    136             low=mid+1;
    137         }
    138     }
    139     return -1;
    140 }
    141 
    142 //  总体来讲判断一个64位数是否是素数,
    143 //  当小于我们已经计算好的素数的最大值时候,只需要在素数数组中查找即可,当大于的时候需要上述方法来判断。
    144 //  假设我们所求的素数是在已经缓存素数的范围内,则在判断是否是素数的时候只需要二分查找即可
    145 int is_prime_with_precompute(unsigned long long Primes[],unsigned long long elem)
    146 {
    147     if (elem<=Primes[PRIME_NUM-1])
    148     {
    149         return (-1!=binary_search_prime(Primes,elem,0,PRIME_NUM-1));
    150     }
    151     else
    152     {
    153         unsigned int j=0;
    154         for (j=0;Primes[j]*Primes[j]<=elem;j++)
    155         {
    156             if (elem%Primes[j]==0)
    157             {
    158                 return 0;
    159             }
    160         }
    161     }
    162     return 1;
    163 }
    164 
    165 //  上面的这种方法已经省去了很多的时间,可以大大帮助判断64位整数是否是素数
    166 //  但是我们在上面的素数定理中也说了,有:
    167 //  x/(lnx-0.5)<π(x)<x/(lnx-1.5)
    168 //    左边不等式对于x>=67成立,右边不等式对于x>√e3≈4.48169...成立.
    169 //  也就是说,我们已知了x之后,我们就可以判断小于x的素数大概有多少个,则我们在查找素数表的时候就不用在[0,PRIME_NUM-1]范围内查找了
    170 //  新的查找范围是[x/(lnx-0.5)-1,x/(lnx-1.5)],则上述算法可以改写如下:
    171 int is_prime_with_precompute_optimize(unsigned long long Primes[],unsigned long long elem)
    172 {
    173     if (elem>67&&elem<=Primes[PRIME_NUM-1])
    174     {
    175         unsigned int 
    176         low=(int)(elem/(log((double)elem)-0.5))-1,
    177         high=(int)(elem/(log((double)elem)-1.5));
    178 
    179         return (-1!=binary_search_prime(Primes,elem,low,high));
    180     }
    181     else
    182     {
    183         unsigned int j=0;
    184         for (j=0;Primes[j]*Primes[j]<=elem;j++)
    185         {
    186             if (elem%Primes[j]==0)
    187             {
    188                 return 0;
    189             }
    190         }
    191     }
    192     return 1;
    193 }
    View Code
  • 相关阅读:
    jq serialize 系列化 乱码 解决办法
    如何使用 TP中的公共函数 (定义在common/common.php中的函数)
    Sublime Text 使用技巧
    动态创建菜单
    jq中写PHP
    redis 管道
    微信强大的demo
    【Hibernate异常处理-1】java.lang.NoSuchMethodError: javax.persistence.JoinColumn.foreignKey()Ljavax/persistence/ForeignKey;
    【Hibernate学习笔记-5.2】使用@Temporal修饰日期类型的属性
    【Hibernate学习笔记-5.1】使用@Transient修饰不想持久保存的属性
  • 原文地址:https://www.cnblogs.com/havePassed/p/3583423.html
Copyright © 2020-2023  润新知