开始正题,接下来我会尝试用自己的理解,去写从纯暴力到筛法的求素数的求解方法。
这里要感谢此篇博客教给我的思想,让我对求质数算法乃至所有算法一个新的认识。
大佬博客
Tips: 因为时间安排原因,我这篇博客讲解的求质数的方法都是应用于:给你一个数n,让你求出n内的所有质数。
如果您是大佬,请跳过前面的试除法,直接跳到后面您需要的地方。
试除法(纯暴力)
好吧,我以前一直以为这个叫做纯暴力(其实也是纯暴力)。
思路: 判断一个数x是否为质数,那么就从2开始到x-1,如果有一个能够除,那么x便是合数,否则为质数。
bool f(int x)
{
for(int i=2; i<x; i++)
if(x%i==0)
return false;
return true;
}
这种方法很简单,也是最容易想到的方法。但是缺点显而易见,时间复杂度太高,所以我们有聪明一点的方法。
试除法2.0
我们很容易想到,x如果连他的1/2都无法整除了,那么肯定后面的也无法整除,于是我们可以将循环设为x/2;
bool f(int x)
{
for(int i=2; i<x/2; i++)
if(x%i==0)
return false;
return true;
}
啧,很容易看到,省了一半的时间了。但是吧,还是不够,毕竟有的时候给你贼大的数咋办,然后我们便有了更加更加聪明的暴力
试除法3.0
我们可以知道,偶数肯定不是质数的,那么只有奇数可能是质数。
那么我们就判断除2以外的n的范围内的奇数,又可以省了大量的时间。
试除法4.0
我们要判断一个数是否为质数,我们可以发现,其实循环只要到sqrt(x)的位置,就ok了。
Why: 其实也很简单,我们可以把一个数拆成两个数相加,也就是因数会是一一对应的关系,而一个因数如果是小于sqrt(x)的,那么另外一个必定是大于sqrt(x)的。
此处代码不贴了,和上就是改个循环。
试除法5.0
我们可以发现,把4和5结合起来,又是一种新的方法是吧,不多说了,开始真正的求质数环节。
分割线
如果你和我说,感觉上面的还是好蠢啊,我还想优化,咋整啊。
emmm,其实我以前也没想到,今天看了博客才学到的,我就以我有限的能力尝试讲一讲后面的。
素筛
埃氏筛
我们可以很容易的得出,一个质数的倍数肯定是合数(这不废话嘛),那么我们就可以把这些数给去除,去除完之后,下一个数便是下一个质数。从而大大节省了时间。
代码
#define maxsize 100
#include <bits/stdc++.h>
using namespace std;
bool mark[maxsize];
void sieve(int n)
{
for(int i=2;i<=sqrt(n+0.5);i++)
{
if(mark[i])
continue;
for(int j=i*i;j<=n;j+=i)
mark[j]=true;
if(i*i>n)
break;
}
}
int main()
{
int n;
scanf("%d",&n);
sieve(n);
for(int i=2;i<=n;i++)
{
if(!mark[i])
printf("%d ",i);
}
printf("\n");
}
虽然这个埃氏筛,感觉已经解决了这个问题,但是如果你想精益求精,你就会发现,此方法会重复筛去同一个数值。比如6,它被2和3筛去了一次。那么咱们要如何解决这个问题呢,请看下面。
欧拉筛
emmm,这个我现在还不会,先留着空,留待日后补充。
最后修改时间:2020.3.20
好吧,我今天晚上又看懂了。
代码:
#include <bits/stdc++.h>
#define maxn 100
using namespace std;
int prime[maxn],cnt=0;
bool p[maxn];
void sieve(int n)
{
for(int i=2;i<=n;i++)
{
if(!p[i])
prime[cnt++]=i;
for(int j=0;j<cnt;j++){
if(i*prime[j]>n)
break;
p[i*prime[j]]=true;
if(i%prime[j]==0)
break;
}
}
}
int main()
{
int n;
scanf("%d",&n);
sieve(n);
for(int i=0;i<cnt;i++)
printf("%d ",prime[i]);
printf("\n");
}
非常牛皮,先放着,今天收工睡觉。
the lastest update 2020.3.20