第一题: 单个素数判定
思路:
- 素数定义:素数又称质数,指只能被 1 和其 自身 整除的大于1的正整数。注意:1 不是素数 , 2 是素数
- 传统思路:因为素数只能表示为 1 * a (a 为其本身)这种因式表达形式,所以需要遍历,从1(不包括 1)开始到 a - 1 的所有正整数,如若存在 n * m = a 则,a不是素数。
- 较好的思路:因为素数的判断方法,为从 [ 2 , a-1 ] 逐个判断是否存在 n * m = a 这种因式,所以,我们只需要判断从 [ 2 , sqrt(a) ] 之间的数即可,因为若存在一个因式 n * m = a 这种因式,当 m >sqrt(a) 时,n 必然小于 sqrt(a),如果 n > sqrt(a) 则 n * m > a 。所以就不需要考察 ( sqrt(a) , a-1 ] 这之间的数了,因为当考查他们的时候,其实他们在之前另一个因数的时候就已经考察过了,再次考察,只会浪费时间。
代码如下:
#include<stdio.h>
#include<math.h>
bool judge(int a){
if(a<=1)return false;
int bound=(int)sqrt(a)+1;//计算枚举上届,为防止double值带来的精度损失。
//所以采取根号值取整后+1的方法,所以宁愿多枚举一个也不能少枚举一个。
for(int i=2;i<bound;i++){//此时就不用等号了,因为bound 一定大于sqrt(a)
if(a%i==0)return false;
}
return true;
}
int main(){
int a;
while(scanf("%d",&a)!=EOF){
printf(judge(a)==true?"YES\n":"NO\n");
}
return 0;
}
注意: 这里有一个小技巧,就是int bound=(int)sqrt(a)+1;
因为sqrt()函数为比较耗时的函数之一,如果将sqrt()函数写进循环里,则每一次循环都要执行一次sqrt()函数,如果写在外面,则只需要执行一次。
第二题: 区间素数判定
思路: 这里有一句非常重要的一句话:若一个数不是素数,则必存在一个素数为其因数。 这句话是素数筛法的核心,即如果已经判断了一个数为素数,则是其的倍数的数均不是素数。换而言之,如果当前所判断的数,不存在小于它的任何素数为其因数,则其为素数。
代码如下:
#include<stdio.h>
int prime[10000];
int primeSize;
bool mark[10001];
void init(){//这个函数是核心代码,相当于预处理,提高时间效率
for(int i=1;i<=10000;i++){
mark[i]=false;
}//初始化,将所有标记数组置为false
primeSize=0;
for(int i=2;i<=10000;i++){
if(mark[i]==true)continue;//被标记过了,说明不是素数
prime[primeSize++]=i;
for(int j=i*i;j<=10000;j+=i){
mark[j]=true;
}
}
}
int main(){
init();
int n;
while(scanf("%d",&n)!=EOF){//输入n,然后进行格式调整
bool isOutput=false;
for(int i=0;i<primeSize;i++){
if(prime[i]<n&&prime[i]%10==1){
if(!isOutput){
isOutput=true;
printf("%d",prime[i]);
}
else{
printf(" %d",prime[i]);
}
}
}
if(isOutput==false){
printf("-1\n");
}else{
printf("\n");
}
}
return 0;
}
注意:
此处有一个难点:
就是为什么当确定一个数为素数的时候,在标记其倍数的时候是从i * i
开始的,而不考虑 ( i-1) * i ,首先,需要明确,我们写这个循环是为了标记某一个数是否为非素数,目的是为了标记,当对于某一个非素数 m 其因式分解为 i * k (k < i),因为我们的目的是为了标记,所以当 i 这个游标为 k 时,就已经把 m 给标记了,所以我们只需要从 i * i 开始即可。