• 判断质数的几种方法


      根据维基百科定义,质数(Prime number),又称素数,指在大于1的自然数中,除了1和此整数自身外,无法被其他自然数整除的数(也可定义为只有1和本身两个因数的数)。比1大但不是素数的数称为合数。1和0既非素数也非合数。质数在公钥加密算法(如RSA)中有重要的地位。

      下边将会介绍几种较为常见的判断质/素数的方法:

      1. 法一:最直接也最笨的方法

      法一是按照质数的定义来考虑的,具体程序见下:

     1 //*********************************** method 1 ***********************************//
     2 bool IsPrime::isPrime_1(uint num)
     3 {
     4     bool ret = true;
     5     for (uint i = 2; i < num - 1; i++)
     6     {
     7         if (num % i == 0)
     8         {
     9             ret = false;
    10             break;
    11         }
    12     }
    13 
    14     return ret;
    15 }

      2. 法二:将循环判断次数减少一半(大约)

      对于一个正整数num而言,它对(num/2, num)范围内的正整数是必然不能够整除的,因此,我们在判断num的时候,没有必要让它除以该范围内的数。代码如下:

     1 //*********************************** method 2 ***********************************//
     2 bool IsPrime::isPrime_2(uint num)
     3 {
     4     bool ret = true;
     5     uint ubound = num / 2 + 1;
     6     for (uint i = 2; i < ubound; i++)
     7     {
     8         if (num % i == 0)
     9         {
    10             ret = false;
    11             break;
    12         }
    13     }
    14 
    15     return ret;
    16 }

      3. 法三:在法二的基础上继续提高

      对于一个小于num的正整数x,如果num不能整除x,则num必然不能整除num/x (num = num/x * x)。反之相同。我们又知num =√num*√num。 如果n除以大于√num的数,必得到小于√num的商,而小于√num的整数已经在2到√num的整数试过了,因为就没有必要再试(√num, num)范围内的数了。代码如下:

      注:经常会看到别人说“一个数 n 如果是合数,那么它的所有的因子不超过sqrt(n)”。这句话是错误的。举一个例子,16的因子包括了1、2、4、8,但很明显8>√16。另外,因子跟因数是不一样的,因数还会包括数本身,如16的因数为1、2、4、8、16。

     1 //*********************************** method 3 ***********************************//
     2 bool IsPrime::isPrime_3(uint num)
     3 {
     4     bool ret = true;
     5     uint ubound = sqrt(num) + 1;
     6     for (uint i = 2; i < ubound; i++)
     7     {
     8         if (num % i == 0)
     9         {
    10             ret = false;
    11             break;
    12         }
    13     }
    14 
    15     return ret;
    16 }

      4. 法四:考虑偶数的因素

      我们都知道,除了2之外,其他所有的偶数(正整数)全都不是质数,因为它们都能被2整除。代码改进如下:

     1 //*********************************** method 4 ***********************************//
     2 bool IsPrime::isPrime_4(uint num)
     3 {
     4     bool ret = true;
     5     if (num == 2)
     6         return ret;
     7 
     8     // it is no need to consider even numbers larger than 2
     9     if (num % 2 != 0)
    10     {
    11         uint ubound = sqrt(num) + 1;
    12         for (uint i = 2; i < ubound; i++)
    13         {
    14             if (num % i == 0)
    15             {
    16                 ret = false;
    17                 break;
    18             }
    19         }
    20     }
    21     else
    22     {
    23         ret = false;
    24     }
    25 
    26     return ret;
    27 }

      5. 法五:埃拉托斯特尼筛选法

      当我们判断某个取值范围内的素数有哪些的时候,有一个方法非常可行,就是埃拉托斯特尼筛选法。这个算法效率很高,但占用空间较大。

      我们知道,一个素数p只有1和p这两个约数,并且它的约数一定不大于其本身。因此,我们下边方法来筛选出来素数:

      1)把从2开始的、某一范围内的正整数从小到大顺序排列;
      2)剩下的数中选择最小的素数,然后去掉它的倍数

      3)依次类推,直到循环结束。

      这种筛选法动态图如下:

      

      程序如下:

     1 //*********************************** method 5 ***********************************//
     2 // find prime numbers between [lower bound, upper bound)
     3 vector<uint> IsPrime::retPrime_5(uint lbound, uint ubound)
     4 {
     5     assert(lbound >= 0);
     6     assert(ubound >= 0);
     7     assert(lbound <= ubound);
     8 
     9     vector<bool> isprime;
    10     for (int i = 0; i < ubound; i++)
    11         isprime.push_back(true);
    12 
    13     for (int i = 2; i < ubound; i++)
    14     {
    15         for (int j = i + i; j < ubound; j += i)
    16         {
    17             isprime[j] = false;
    18         }
    19     }
    20 
    21     vector<uint> ret;
    22     for (int i = lbound; i < ubound; i++)
    23     {
    24         if (i != 0 && i != 1 && isprime[i])
    25             ret.push_back(i);
    26     }
    27 
    28     return ret;
    29 }

      6. 法六:去除法五中不必要的循环

      对于法五来说,即使isprime中已经被判断为false的元素,它以及它的倍数还会被重新赋值为false(可能会有很多遍),而实际上已经没有必要这样子做。例如,第2个元素的倍数第4、6、8、10...个元素已经被判定为false,但循环到第4个元素的时候,第8、12、16...个元素还会被重新赋值,这有点重复。因此,我们要去掉这些重复的工作。代码比较简单,只需要加一语句即可,见下:

     1 //*********************************** method 6 ***********************************//
     2 // find prime numbers between [lower bound, upper bound)
     3 vector<uint> IsPrime::retPrime_6(uint lbound, uint ubound)
     4 {
     5     assert(lbound >= 0);
     6     assert(ubound >= 0);
     7     assert(lbound <= ubound);
     8 
     9     vector<bool> isprime;
    10     for (int i = 0; i < ubound; i++)
    11     {
    12         if (i < 2)
    13             isprime.push_back(false);
    14         else
    15             isprime.push_back(true);
    16     }
    17 
    18     for (int i = 2; i < ubound; i++)
    19     {
    20         if (isprime[i])
    21         {
    22             for (int j = i + i; j < ubound; j += i)
    23             {
    24                 isprime[j] = false;
    25             }
    26         }
    27     }
    28 
    29     vector<uint> ret;
    30     for (int i = lbound; i < ubound; i++)
    31     {
    32         if (isprime[i])
    33             ret.push_back(i);
    34     }
    35 
    36     return ret;
    37 }

      7. 法七:大综合(结合法三及法六)

       法七是结合了法三及法六,代码如下:

     1 //*********************************** method 7 ***********************************//
     2 // find prime numbers between [lower bound, upper bound)
     3 vector<uint> IsPrime::retPrime_7(uint lbound, uint ubound)
     4 {
     5     assert(lbound >= 0);
     6     assert(ubound >= 0);
     7     assert(lbound <= ubound);
     8 
     9     vector<bool> isprime;
    10     for (int i = 0; i < ubound; i++)
    11     {
    12         if (i < 2)
    13             isprime.push_back(false);
    14         else
    15             isprime.push_back(true);
    16     }
    17 
    18     uint ulimit = sqrt(ubound) + 1;
    19     for (int i = 2; i < ulimit; i++)
    20     {
    21         if (isprime[i])
    22         {
    23             uint repeat = ubound / i;
    24             for (int j = 2; j < repeat; j++)
    25             {
    26                 isprime[i * j] = false;
    27             }
    28         }
    29     }
    30 
    31     vector<uint> ret;
    32     for (int i = lbound; i < ubound; i++)
    33     {
    34         if (isprime[i])
    35             ret.push_back(i);
    36     }
    37 
    38     return ret;
    39 }

      整个程序代码(包括单元测试代码)见Github.

      更多的方法请参见百度文库上的一篇文章

  • 相关阅读:
    bzoj2002: [Hnoi2010]Bounce 弹飞绵羊 [分块][LCT]
    luoguP1886 滑动窗口 [单调队列]
    bzoj1047: [HAOI2007]理想的正方形
    bzoj1012: [JSOI2008]最大数maxnumber [单调队列]
    树与二叉树之二--二叉树的性质与存储
    树与二叉树之一--基本概念与存储结构
    Markdown段首空格
    C++ atan2
    凸包学习笔记
    Codeforces Round #545 (Div. 1) E. Train Car Selection
  • 原文地址:https://www.cnblogs.com/xiehongfeng100/p/4332998.html
Copyright © 2020-2023  润新知