求质数的方法(从暴力到素筛)

开始正题,接下来我会尝试用自己的理解,去写从纯暴力到筛法的求素数的求解方法。
这里要感谢此篇博客教给我的思想,让我对求质数算法乃至所有算法一个新的认识。
大佬博客
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值