今天想总结一下素数的几种求法
暴力方法:
最简单的方法就是从2开始循环到n,如果没有一个数能被n整除就是素数
#include<stdio.h>
int isPrime(int n) {
for(int i=2; i<n; i++) {
if(n%i == 0)
return 0 ;
}
return 1 ;
}
int main() {
int x ;
scanf("%d", &x) ;
if(isPrime(x))
printf("YES\n") ;
else
printf("NO\n") ;
return 0 ;
}
这个方法是最简单粗暴的,但是也是效率最低的需要循环到n-1,可以进行一点改进,循环的判断条件只需要到n/2+1就可以了
int isPrime(int n) {
for(int i=2; i<n/2+1; i++) {
if(n%i == 0)
return 0 ;
}
return 1 ;
}
这样循环的次数还是不够少,其实只需要判断到根号n次就行,一般来说可以使用sqrt(n)来求根号n,我们还可以用 i*i<n来实现
int isPrime(int n) {
for(int i=2; i*i<n; i++) {
if(n%i == 0)
return 0 ;
}
return 1 ;
}
其实还可以继续优化这个方法,因为除了2以外的所有偶数都不可能是素数,所以我们只需要判断奇数就行
bool isPrime(int n) {
if(n%2==0 && n!=2) //除了2以外的所有偶数都不是素数
return false ;
else if(n == 2)
return true ;
for(int i=3; i*i<n; i+=2) { //3-根号n内没有一个数能被n整除就是素数
if(n%i == 0)
return false ;
}
return true ;
}
这是我目前能想到用暴力的方法最优的样子,如果还有可以优化的地方欢迎指教
埃氏筛:
这个方法是通过打表找出指定范围内所有的素数,当数字范围小的时候用暴力没问题但只要范围太大就用不了了,就可以用这个方法
原理:从2开始循环,将2存入素数表中,把2所有的倍数都舍去,然后对3进行相同的操作,一直这样循环到头
#include<cstdio>
int isPrime[100000] ; //记录该点是否是素数
int prime[100000] ; //存素数
int main() {
int count = 0 ; //记录共有几个素数
for(int i=2; i<100000; i++) {
if(isPrime[i] == 0) { //从2 开始将2的所有倍数除去然后是3的倍数4的倍数…………
prime[count++] = i ; //将素数存入
for(int j=i*i; j<100000; j+=i)
isPrime[j] = 1 ;
}
}
for(int i=0; i<count; i++) {
printf("%d ",prime[i]) ;
if(i%10 == 0)
printf("\n") ;
}
return 0 ;
}
第二层for循环中j 从 i * i 而不是从 i + i开始,因为 i*(2~ i-1)在 2~i-1时都已经被筛去,所以从i * i开始
这个方法有个缺点就是会重复多次筛去一个点,于是就有了下一个方法
欧拉筛:
这个方法是在埃氏筛的基础上做了一点改进,原理是把一个合数(6=2*3)通过它的最小质因子筛去
#include<cstdio>
int isPrime[100000] ;
int prime[100000] ;
int main() {
int count = 0 ;
for(int i=2; i<100000; i++) {
if(isPrime[i] == 0)
prime[count++] = i ;
for(int j=0; j<count&&i*prime[j]<100000; j++) { //找出由i和已得到的素数相乘得到的数
isPrime[i*prime[j]] = 1 ; //将由i和最小质因数相乘得到的数排除,
if(i%prime[j] == 0) //如果i能被已经存过的素数整除,说明在之前就由其他的i和最小质因数相乘过,不用重复查
break ;
}
}
for(int i=0; i<count; i++) {
printf("%d ",prime[i]) ;
if(i%10 == 0)
printf("\n") ;
}
return 0 ;
}
总结:
通过写这个求素数的不同方法,从低效的逐渐提高效率,也反应了学习编程一年的一些想法上的进步,这样看上去还是比较开心的。所以说考虑一个问题还是要多研究多考虑深一些,不断优化自己的代码