定义
质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定1既不是质数也不是合数)。
问题
给定一个数字n,我们如何判断这个数字是不是素数?
思路
我们已经知道了素数的定义,那么我们只需要
从2到n - 1依次枚举所有的数字,看看能否被n整除
,如果能够被n整除,那么就说明不是质数,否则说明是质数。
解题代码
#include <stdio.h>
int main()
{
int n, sign = 0;
scanf("%d",&n);
for(int i = 2; i < n; ++i)
{
if(n < 2 || n % i == 0)
{
sign = 1;
break;
}
}
if (1 == sign)
{
printf("不是素数\n");
}
else
{
printf("是素数\n");
}
return 0;
}
这里的flag用于起标记的作用,标记是否为素数。
思考
可以发现时间复杂度是0(n)。那么我们如何优化一下?
假设一个数字
n
n
n,它的因子是
x
x
x,那么另外一个因子是
n
x
{n} \over {x}
xn,那么两个因子一定存在一个大小关系,我们假设是
x
≤
x ≤
x≤
n
x
{n} \over{x}
xn ,那么我们可以得到
x
≤
x≤
x≤
n
\sqrt{n}
n 。
注意:
这里会有人这么做
:
:
:那我假设
n
x
{n} \over {x}
xn
≤
x
≤x
≤x,那么我得到
x
≥
x≥
x≥
n
\sqrt{n}
n,那该怎么继续向下分析呢?
得到这个结果不假,是真的。意味着这样做的话,得到的
x
x
x范围是
x
≥
x≥
x≥
n
\sqrt{n}
n,那么说明
x
x
x的范围是
x
>
=
x >=
x>=
n
\sqrt{n}
n并且
x
≤
x≤
x≤
n
n
n。既然这样的话,那么另外一个因子
n
x
{n} \over {x}
xn的范围就是
n
x
{n} \over{x}
xn
≤
≤
≤
n
\sqrt {n}
n。
经过这样的分析,我们可以发现,一个数$n$的因子一定是有一个在小于$\sqrt{n}$的范围内,一个一定是在$≥$$\sqrt{n}$的范围内。
那么既然分析到这里,我们可以这样做:
只枚举小于等于 n \sqrt{n} n的范围内的数字,因为将这个范围内的数字枚举一遍的话,也就等效地将 n \sqrt{n} n到n这个范围内地数字给判断了一边。比如说我们发现 n \sqrt{n} n之前的所有数字都没有被整除,那么也就说明 n \sqrt{n} n到n之间的数字也不会被整除,因为如果要是 n \sqrt{n} n范围之前有的数字被整除掉的话,那么也就意味着一定有 n \sqrt{n} n到n之间有另外一个因子和它对应。如果 n \sqrt{n} n到n之间有数字被整除,那么也就意味着 n \sqrt{n} n之前有另外一个因子和它对应。这里发现 n \sqrt{n} n之前的数字没有被整除,所以意味着 n \sqrt{n} n到n之间也没有数字被整除。
优化
我们只需要从2枚举到 n \sqrt{n} n即可。
代码
#include <stdio.h>
int is_prime(int num)
{
for(int i = 2; i <= num / i; ++i)//(1)
{
if (0 == num % i)
{
return 0;
break;
}
}
return 1;
}
int main()
{
int n;
scanf("%d",&n);
if(!is_prime(n))
{
printf("不是素数\n");
}
else
{
printf("是素数\n");
}
return 0;
}
- 注意这里的写法。枚举到
n
\sqrt{n}
n的话,不推荐使用
sqrt(n)
和i * i <= num
这两种写法。 sqrt(n)
每次都要调用库函数,那么速度就会慢一些。i * i <= n
,当i很大的时候,可能会发生溢出,那么 i * i就有可能是负数,那么也会执行 i * i <= n这个语句,明显发生错误。- 推荐博客中的这种写法。
写在最后
这里的优化,感觉自己写的不好。希望大家尽力理解。最后,为大家附上一张图片,帮助大家理解。
如图:
如果n是合数,那么它一定会有除了1和它本身之外的因子。这些因子是成对出现的
,那么就如图中那样,红色的因子和蓝色的因子都是一对一对出现的。看完这个,可能会对优化那里的理解有帮助。