• 素数的几种求法


    1.先给出一种大家都熟知的简单求法,就是枚举判定,然后从正整数2开始往后选即可:

     1 bool isprime(int a){
     2     if(a<2){
     3         return 0;
     4     }
     5     int temp=floor(sqrt(a) +0.5);
     6     for(int i=2;i<=temp;i++){
     7          if(a%i==0) return 0;
     8     }
     9     return 1;
    10 }

    2.改进一点的是打表法筛选,这个算法的思想就是,先利用素数的性质,筛去一些不合格的数,总的说来其本质上还是第一种算法。

     1 bool isprime2(unsigned n){
     2     if ( n < 2 ){ 
     3     // 小于2的数即不是合数也不是素数
     4         throw 0;
     5     }
     6     if(n==2){
     7         return true;
     8     }
     9     static unsigned aPrimeList[] = { // 素数表
    10          2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
    11         43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 113, 
    12         193, 241, 257, 337, 353, 401, 433, 449, 577, 593, 641, 
    13         673, 769, 881, 929, 977, 1009, 1153, 1201, 1217, 1249, 
    14         1297,1361, 1409, 1489, 1553, 1601, 1697, 1777, 1873, 
    15         1889, 2017, 2081, 2113, 2129, 2161, 2273, 2417, 2593, 
    16         2609, 2657, 2689, 2753, 2801, 2833, 2897, 3041, 3089, 
    17         3121, 3137, 3169, 3217, 3313, 3329, 3361, 3457, 3617, 
    18         3697, 3761, 3793, 3889, 4001, 4049, 4129, 4177, 4241, 
    19         4273, 4289, 4337, 4481, 4513, 4561, 4657, 4673, 4721, 
    20         4801, 4817, 4993, 5009, 5153, 5233, 5281, 5297, 5393, 
    21         5441, 5521, 5569, 5857, 5953, 6113, 6257, 6337, 6353, 
    22         6449, 6481, 6529, 6577, 6673, 6689, 6737, 6833, 6961, 
    23         6977, 7057, 7121, 7297, 7393, 7457, 7489, 7537, 7649, 
    24         7681, 7793, 7841, 7873, 7937, 8017, 8081, 8161, 8209, 
    25         8273, 8353, 8369, 8513, 8609, 8641, 8689, 8737, 8753, 
    26         8849, 8929, 9041, 9137, 9281, 9377, 9473, 9521, 9601, 
    27         9649, 9697, 9857 
    28     };
    29     
    30     const int nListNum = sizeof(aPrimeList)/sizeof(unsigned);//计算素数表里元素的个数
    31     for (unsigned i=0;i<nListNum;++i ){ 
    32         if(n==aPrimeList[i]){
    33             return true;
    34         }
    35         if(0==n%aPrimeList[i]){
    36             return false;
    37         }
    38     }
    39     
    40     /*由于素数表中元素个数是有限的,那么对于用素数表判断不到的数,就只有用笨蛋办法了*/
    41     unsigned temp=floor(sqrt(n)+0.5);
    42     for (unsigned i=aPrimeList[nListNum-1];i<temp;i++ ){ 
    43         if (0==n%i){ 
    44         // 除尽了,合数 
    45             return false;
    46         }
    47     }
    48     return true; 
    49 } 

    3.上大招!费马测试判定算法,虽然“n能通过的费马测试”仅仅是“n是素数”的必要条件,但是可以通过增加测试次数,从而降低出错概率。可观的出错概率会降低至1/2^k以下,其中k是测试次数。

     1 //快速计算a^b%c 
     2 unsigned fastmod(unsigned a,unsigned b,unsigned c){
     3     unsigned num=1;
     4     a%=c;//这里不进行初始化也是可以的
     5     
     6     while(b>0){
     7         if(b%2==1){
     8             num=(num*a)%c;    
     9         }
    10         b/=2;     //这一步将b->log2(b) 
    11         a=(a*a)%c;
    12     }
    13     return num;
    14 }
    15 
    16 bool isprime(unsigned n){
    17     if ( n < 2 ){ // 小于2的数即不是合数也不是素数
    18         throw 0;
    19     }
    20     
    21     //这里选取了50个素数进行测试,原则上只要选取小于n个数即可(没有必须是素数的要求,
    22     //     选取小一点的数比如100以内容易计算) 
    23     static unsigned aPrimeList[] = {
    24         2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
    25         31, 37, 41,43, 47, 53, 59, 61, 67, 71, 
    26         73, 79, 83, 89, 97, 113, 193, 241, 257, 337,
    27        353, 401, 433, 449, 577, 593, 641, 673, 769, 881,
    28         929, 977, 1009, 1153, 1201, 1217, 1249, 1297,1361, 1409
    29     };
    30     
    31     const int nListNum = sizeof(aPrimeList) / sizeof(unsigned);
    32     for (int i=0;i<nListNum;++i){ // 按照素数表中的数对当前素数进行判断
    33         if(n==aPrimeList[i]) return true;
    34     }
    35     
    36     for (int i=0;i<nListNum;++i){ // 按照素数表中的数对当前素数进行判断
    37     
    38         if (1!=fastmod(aPrimeList[i],n-1,n)) // 费马测试,测试a^n-1=1mod n ,如果符合则满足n是素数的必要条件 
    39         {
    40             return false;
    41         }
    42     }
    43     return true;
    44 }

     不过上面这个费马测试判定素数的算法是有bug的,一些称为卡米切尔数的合数总能通过上述测试,在一亿范围内只有255个这样的捣蛋鬼。

    有关卡米切尔数的合数的相关信息和解决办法这里给出链接地址:

    卡米切尔数的合数:http://www.tceic.com/l2208ljg9ki3j5hhlj0g7444.html

    解决办法是使用拉宾米勒测试(其实是另一种高阶的素数判定算法):http://www.cnblogs.com/skyivben/archive/2010/07/10/1775001.html

    4.筛法求素数:

     1 #include <iostream>
     2 #include <cstring>
     3 using namespace std;
     4 
     5 //筛法求素数 
     6 #define N  100000
     7 int valid[N],primers[N];
     8 int count=0;
     9 
    10 void GenPrimer(int n){                //参数n代表找出n以内的所有素数 
    11     int i,j,k;
    12     for(i=2;i<=n;i++){                //初始化,将valid[n]的值赋为1 
    13         valid[i]=true;     
    14     } 
    15 
    16     for(i=2;i*i<=n;i++){              //从2~sqrt(n) 进行筛选 
    17         if(valid[i]){                 //从(valid[i] ) 素数i开始  
    18             for(j=i*i;j<=n;j+=i){     //从i^2开始,之前搜过的不再重复;将i*i、i*(i+1)、i*(i+2)、i*(i+3)...统统筛掉 
    19                 valid[j]=false;        
    20             }
    21         }
    22     }
    23     
    24     for(i=2;i<=n;i++){
    25         if(valid[i]){
    26             primers[count++]=i;
    27         }
    28     }
    29 } 
    30 
    31 int main(){
    32     memset(primers,-1,sizeof(primers));//初始化 
    33     GenPrimer(7000);                  //找出7000以内的所有素数。 
    34     
    35     for(int i=0;i<count;i++){
    36         cout<<primers[i]<<" ";
    37         if((i+1)%10==0) cout<<endl;
    38     }
    39 }    
    40     

     5.今天搜索的时候,发现了一种新的筛法,快速线性筛法。(因为上述筛法在筛选的时候,会有重复晒算的情况发生,所以可以进行优化,这里的算法就是其中一种优化方法。)         这种筛法没有冗余,不会重复筛同一个数,所以几乎是线性的,虽然从算法上分析,它的时间复杂度并不是O(n).

     1     
     2 #include <cstring>
     3 const int MAXN = 1e6+10;            //该算法可以快速筛出MAXN以内的所有素数 
     4 bool u[MAXN];                       //若i为素数则标记为1.;
     5 int prime[MAXN], cnt = 0;           //cnt记录素数个数 ,prime[]数组记录素数 
     6 
     7 void primer(){
     8     memset(u, true, sizeof(u));
     9     
    10     for(int i=2; i<MAXN; ++i)
    11     {
    12         if(u[i]) prime[cnt++] = i;    //用prime数组记录素数 
    13         for(int j=0; j<cnt && i*prime[j]<MAXN; ++j)
    14         {
    15             u[i*prime[j]] = false;    //把素数prime[j]的i倍的数全部筛掉   
    16             if(0 == i%prime[j]) break;//符合该条件的i已经标志prime[i]=0,故无需重复标记   
    17         }
    18     }
    19 }

    能不能更进一步呢。。答案是肯定的,既然筛得时候2的倍数,3的倍数都不是,那么只需要处理6n+1,6n+5就可以了。

    然后结合线性筛法有以下代码:

     1 #include <iostream> 
     2 #include <stdlib.h>
     3 #include <cmath>
     4 
     5 unsigned *div_prime(unsigned N, unsigned *count){
     6     int i, j, g;
     7     int isprime = 1;
     8     double t;
     9     unsigned total; /* count of prime number, init is 3 */
    10     unsigned *prime;
    11 
    12     /* estimate the number of primes */
    13     /* 分配内存,分配的大小用N*1.17/log(N)来计算,这个公式是根据素数定理来的,这里要注意的是1.17这个数字,如果使用这个数字,则N最好要大于1000,不然会出问题 */
    14     prime = malloc(sizeof(unsigned) * (int)(N*1.17/log(N)));   
    15     if(prime == NULL){
    16         fprintf(stderr, "div_prime: Insufficent memory!
    ");
    17         exit(0);
    18     }
    19     prime[0] = 2; prime[1] = 3, prime[2] = 5;
    20     total = 3; /* found prime's number */
    21     /* 明显偶数不是素数(2除外),3的倍数也不是素数,2*3的倍数也不是素数,即我们不用判断6n形式的数,只需判断6n+1,6n+2,6n+3,6n+4,6n+5,但这里面6n+2和6n+4是2的倍数,不需判断,6n+3是3的倍数不需判断,因此只需要判断具有6n+1和6n+5的数是不是素数,即只判断数7, 11, 13, 17, 19, 23....,这里两个数的间隔是2和4的交替。 */
    22      g = 2;     
    23     for(i = 7; i < N; i+=g)
    24     {
    25         g = 6 - g; /* only check 6i+1 and 6i+5 */
    26         for(j = 0; prime[j] * prime[j] <= i; j++)
    27             if(i % prime[j] == 0)
    28             {
    29                 isprime = 0; /* non-prime */
    30                 break;
    31             }
    32         if(isprime == 0)
    33             isprime = 1;
    34         else
    35             prime[total++] = i; /* isprime is 1, i is prime */
    36     }
    37     *count = total;       /* count 保存找到的素数的个数,这个通过参数返回 */
    38     return prime; 
    39 }

    ural 1086  Cryptography

    这道题刚开始还以为用上述第一个函数枚举判定素数会超时,结果忙活了大半天,后来发现竟然不会超时。

     1 #include <iostream>
     2 #include <cmath>
     3 #include <cstring>
     4 using namespace std;
     5 
     6 
     7 bool isprime(int a){
     8     int temp=floor(sqrt(a) +0.5);
     9     for(int i=2;i<=temp;i++){
    10          if(a%i==0) return 0;
    11     }
    12     return 1;
    13 }
    14 
    15 int main(){
    16     int k,count=0;
    17     cin>>k;
    18     int num[k];
    19     int maxnum=0;
    20     for(int i=0;i<k;i++){
    21         cin>>num[i];
    22         if(num[i]>maxnum) maxnum=num[i];
    23     }
    24     
    25     long primers[maxnum];
    26     for(long i=2;;i++){
    27         if(isprime(i)){
    28             primers[count++]=i;
    29         }
    30         if(count==maxnum){
    31             break;
    32         }
    33     }
    34     
    35     for(int i=0;i<k;i++){
    36         cout<<primers[num[i]-1];
    37         cout<<endl;
    38     }
    39 }

    参考资料:http://blog.chinaunix.net/uid-24631445-id-3035081.html

  • 相关阅读:
    mac安装搜狗
    idea的阿里代码规范检查
    记录windows10闪屏
    github访问不了
    线程安全问题例子
    简单负载均衡工具类
    git将远程分支回归到指定版本
    minio的使用
    php禁止浏览器使用缓存页面的方法
    百度seo
  • 原文地址:https://www.cnblogs.com/liugl7/p/4885341.html
Copyright © 2020-2023  润新知