Mrli
别装作很努力,
因为结局不会陪你演戏。
Contacts:
QQ博客园

素数判别

2019/12/04 ACM竞赛入门
Word count: 1,166 | Reading time: 5min

素数判别

由于1既不是素数也不是合数,所以下面暂未判断。如果需要则需特判如if (num == 1) return false;

方法一
1
2
3
4
5
6
7
8
9
bool isPrime_2( int num )
{
int tmp =sqrt( num);
for(int i= 2;i <=tmp; i++)
if(num %i== 0)
return 0 ;
return 1 ;
}
//一个数若可以进行因数分解,那么分解时得到的两个数一定是一个小于等于sqrt(n),一个大于等于sqrt(n)。若sqrt(n)左侧找不到约数,那么右侧也一定找不到约数
方法二(筛选法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include"cstdio"
#include"cstring"
using namespace std;
#define MAX 100000//求MAX范围内的素数
long long su[MAX],cnt;
bool isprime[MAX];
void prime()
{
cnt=1;
memset(isprime,1,sizeof(isprime));//初始化认为所有数都为素数
isprime[0]=isprime[1]=0;//0和1不是素数
for(long long i=2;i<=MAX;i++)
{
if(isprime[i])//保存素数
{
su[cnt++]=i;
}
for(long long j=i*2;j<=MAX;j+=i)//素数的倍数都为合数
{
isprime[j]=0;
}
}
}
int main()
{
prime();
for(long long i=1;i<cnt;i++)
printf("%d ",su[i]);
return 0;
}
方法三(剪枝)

证明:令x≥1,将大于等于5的自然数表示如下:

······ 6x-1,6x,6x+1,6x+2,6x+3,6x+4 | 6x+5,6(x+1),6(x+1)+1 ······

可以看到,不在6的倍数两侧,即6x两侧的数为6x+2,6x+3,6x+4,由于2(3x+1),3(2x+1),2(3x+2),所以它们一定不是素数,再除去6x本身,显然,素数要出现只可能出现在6x的相邻两侧。这里有个题外话,关于孪生素数,有兴趣的道友可以再另行了解一下,由于与我们主题无关,暂且跳过。这里要注意的一点是,在6的倍数相邻两侧并不是一定就是质数。===>只需判断6两侧的是不是质数(6x-1、6x+1)

此时判断质数可以6个为单元快进,即将方法(2)循环中i++步长加大为6,加快判断速度,原因是,假如要判定的数为n,则n必定是6x-1或6x+1的形式,对于循环中6i-1,6i,6i+1,6i+2,6i+3,6i+4,其中如果n能被6i,6i+2,6i+4整除,则n至少得是一个偶数,但是6x-1或6x+1的形式明显是一个奇数,故不成立;另外,如果n能被6i+3整除,则n至少能被3整除,但是6x能被3整除,故6x-1或6x+1(即n)不可能被3整除,故不成立。===>综上,循环中只需要考虑6i-1和6i+1的情况(被本身更小的因数所除如22/11),即循环的步长可以定为6,每次判断循环变量k和k+2的情况即可,理论上讲整体速度应该会是方法(2)的3倍。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool isPrime_3( int num )
{
//两个较小数另外处理
if(num ==2|| num==3 )
return 1 ;
//不在6的倍数两侧的一定不是质数
if(num %6!= 1&&num %6!= 5)
return 0 ;
int tmp =sqrt( num);
//在6的倍数两侧的也可能不是质数
for(int i= 5;i <=tmp; i+=6 )
if(num %i== 0||num %(i+ 2)==0 )
return 0 ;
//排除所有,剩余的是质数
return 1 ;
}

for循环i从5开始而不是从7开始的原因:

从5、7开始的区别在于i <= sqrt(num)…如果是5的话,判断条件为25;如果是7的话,判断的条件就为49。

而仔细观察49内的所有质数,发现25之前的质数都是6k左右的数(6k-1,6k+1),而25以后,就不定都有了。如26则不为质数。 所以如果从5开始的话,那么25以内的数 都不会进入for循环,经过if(num %6!= 1&&num %6!= 5)的筛选后,就都是素数了。 而如果是从7开始,那么25-49之内的数不符合条件却不会进入for循环,所以26缺少这个for的循环判断后就被误判为素数了。

==>以我浅薄的数学见识理解,25以内素数规律的巧合性使得 这些数不需要进入for循环判断,所以相比于从7开始的错误,5开始是正确的

给出Py代码
1
2
3
4
5
6
7
8
9
10
import math
def prime_num(num):
if num == 2 or num == 3:
return 1
if num%6 != 1 and num%6!=5:
return 0
for i in range(5,math.ceil(math.sqrt(num))+1,6): #就剩6k-1 和 6k+1
if num%i == 0 or num%(i-2)==0:
return 0
return 1

参考:https://blog.csdn.net/huang_miao_xin/article/details/51331710

Author: Mrli

Link: https://nymrli.top/2018/10/15/素数判别/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
LeetCode 26. 删除排序数组中的重复项
NextPost >
\r - \n - \r\n的区别
CATALOG
  1. 1. 素数判别
    1. 1.0.1. 方法一
    2. 1.0.2. 方法二(筛选法)
    3. 1.0.3. 方法三(剪枝)
    4. 1.0.4. 给出Py代码