2020年11月21日 纪念日——线性筛选质数

2020年11月21日 纪念日——线性筛选质数

有点丢人,讲题之前没有讲明白,然后在很多人之前丢了大脸。以此时作为纪念吧。共勉之。

某个算法,只有思考过,AC过,再思考。才可能明白吧。

开始记录下线性筛选质数:

基础知识

质数

质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

合数

与质数相对的就是合数。

最小因数定理——一个数的最小因数一定是质数

一个大于1的自然数,最小的因数一定是质数。

证明如下:

我们来把它编 成一道证明题 ,并利用列举法和反证法 求证。
求证:除了1之外,一个数的最小dao因数一定是质数。
证明:设任意整数N>0,
若N为质数,则N只有因数1和N,所以除了1之外,N的最小因数是质数N。
若N为合数,除了1之外,设N有最小因数n,
若n为质数,则除了1之外,N的最小因数是质数n。
若n为合数,则除了1和n之外,n必有其它因数,设m为n的因数,
则m也为N的因数,且m<n,所以n为合数时,不可能为N的最小因数。
综上可知,除了1之外,一个数的最小因数一定是质数。

线性筛选的线性解释?

线性:可以在·O(n) 的时间复杂度内求出 1∼n 之间的所有质数。

原理

如果要筛选质数的话,用 已知的质数 去筛出合数。任意一个合数都可以被质因数分解,那么我们就可以用已知的一个质数去筛选出一个以这个质数为最小质因数的合数

为什么是筛选出一个以这个质数为最小质因数的合数呢?

因为一个合数的质因数可能有很多个,但是最小质因数就只有一个。在这个算法里,一个数如果是质数,就不用筛,如果是合数,就只会被它的最小质因数给筛出来。这样就达到的O(n)的复杂度啦。

代码

下面,我们会得到一个st[i],代表i是否是一个合数

primes[i],代表第i个质数的值。

利用primes中质数,来将st中的合数找出来。

int primes[N], cnt;
bool st[N];

void get_primes(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        //如果i是质数,那么加入primes
        if (!st[i]) primes[cnt ++ ] = i;
        for (int j = 0; primes[j] <= n / i; j ++ )
        {
            //primes[j] * i 是合数
            st[primes[j] * i] = true;
            //重要,如果 i能被 primes[j]整除,那么i就可以被 质数primes[j]取代,i这个因数就可以跳过
            if (i % primes[j] == 0) break;
        }
    }
}
 

注意点:

  • 线性是因为,一个合数,只会被自己的最小因数标记出来,最小因数就是质数。(见前面的基础知识)
  • 最重要if (i % primes[j] == 0) break;理解:
    • 1)当i%primes[j]!=0时,说明此时遍历到的primes[j]不是i的质因子,所以primes[j]*i的最小质因子就是primes[j];
    • 2)当有i%primes[j]==0时,说明i的最小质因子是primes[j],因此primes[j]*i的最小质因子也就应该是
      prime[j],之后接着用st[primes[j+1]*i]=true去筛合数时,就不是用最小质因子去更新了,因为i有最小质因子primes[j]<primes[j+1],此时的primes[j+1]不是primes[j+1]*i的最小质因子,此时就应该退出循环,避免之后重复进行筛选。

那么不加if (i % primes[j] == 0) break;,如何:

  • 下图中,相同色块就是重复标记合数位置。标记合数靠 i *primes[j]
  • 但是线性筛选质数,需要合数只能被最小的质因数,标记一次。不能被重复标记 。不加这句话后果很严重

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OgWEV7rT-1606037296975)(2020年11月21日 纪念日——线性筛选质数.assets/1606036571515.png)]

我们,在这里单拿出:

标记12时的情况来说:

  • i=4,primes[2]=3,则3*4=12。但是 4%primes[1] == 0,说明4已经不是12的最小质因数了,因为至少primes[1]更小。当然,primes[1]=2,2肯定时12的最小质因数,而不是4。

  • i=6时,primes[1]=2,此时2*6=12,此时2确实是12的最小质因数。所以12是应该由i=6时候,标记。

下面是加了的情况,就没有重复标记的情况。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0FDWmK4p-1606037296981)(2020年11月21日 纪念日——线性筛选质数.assets/1606036831740.png)]

例题

洛谷

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FwjtoJGJ-1606038896902)(2020年11月21日 纪念日——线性筛选质数.assets/1606037930431.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9kACbuIx-1606038896908)(2020年11月21日 纪念日——线性筛选质数.assets/1606037937904.png)]

代码:

#include<bits/stdc++.h>

using namespace std;

const int N = 1e8 + 10;
int primes[N];
int n, q, cnt;
bool st[N];

void getPrimes() {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) primes[++cnt] = i;
        for (int j = 1; j <= cnt && i * primes[j] <= n; j++) {
            st[i * primes[j]] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

int main() {
    cin >> n >> q;
    int t;
    getPrimes();
    while (q--) {
        cin >> t;
        cout << primes[t] << endl;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值