判断质数的几种方法

  根据维基百科定义,质数(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.

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

 

posted @ 2015-03-12 21:11  峰子_仰望阳光  阅读(29625)  评论(0编辑  收藏  举报