Day 2:数论——[筛法、gcd、lcm 和裴蜀定理] 未完

本文详细讲解了数论中的筛法,包括暴力筛、埃氏筛(埃拉托斯特尼筛法)以及欧拉筛的实现原理、实例演示和时间复杂度分析。重点介绍了如何利用这些方法快速找出素数,以及它们在20以内素数筛选中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Day 2:数论——[筛法、gcdgcdgcdlcmlcmlcm 和裴蜀定理]

一、筛法

注:

1.我们用 ps_is_prime 数组来表示暴力筛的 boolboolbool 类型判断素数数组(000 表示是质数,111 表示不是质数)。

2.我们用 ai_is_prime 数组来表示埃氏筛的 boolboolbool 类型判断素数数组。

2.我们用 ou_is_prime 数组来表示欧拉筛的 boolboolbool 类型判断素数数组,prime 来表示欧拉筛的素数表,cnt 来表示 prime 数组的长度。

1.暴力筛(ViolenceViolenceViolence screeningscreeningscreening methodmethodmethod

①① 原理

CaseCaseCase 111 初始化:一个常识:111 肯定不是质数,那么 ps_is_prime[1] 便置为 111

CaseCaseCase 222 筛数过程:先用 iii 枚举 2−n2 - n2n 中的每一个数,再用 jjj222 开始枚举,将 iii 的所有不超过 nnn 的倍数在 ps_is_prime 中标记为 111

CaseCaseCase 333 小优化:由于每个数的因数都成对出现,所以只需要枚举到 n\sqrt nn 即可。

②② 具体例子举例

例如:筛 202020 以内的质数。

首先,将 111 表示不是质数,预先算出 n=4\sqrt n = 4n=4

1−n1 - n1n1234567891011121314151617181920
ps_is_prime10000000000000000000

第一次循环,i=2i = 2i=2

1−n1 - n1n1234567891011121314151617181920
ps_is_prime10010101010101010101

第二次循环,i=3i = 3i=3

1−n1 - n1n1234567891011121314151617181920
ps_is_prime10010101110101110101

第三次循环,i=4i = 4i=4

1−n1 - n1n1234567891011121314151617181920
ps_is_prime10010101110101110101
③③ 代码展示
#include <bits/stdc++.h>
using namespace std;
const int N = 2e7 + 5;
int n;
bool ps_is_prime[N];
void ps_get(int m) {
	ps_is_prime[1] = 1;
	int nn = sqrt(m);
	for (int i = 2;i <= nn;i++) {
		for (int j = 2;j * i <= m;j++) {
			ps_is_prime[i * j] = 1;
		}
	}
}
int main() {
	cin >> n;
	ps_get(n);
	return 0;
}
④④ 时间复杂度及证明

个人的一些理解,不要在意其准确性。

其实课堂上,郭老师说,外层循环从 2−n2 - n2n 改为 2−n2 - \sqrt n2n 时间复杂度有很大区别,但实际上区别很小。别急,先看我证明。

若是 2−n2 - n2n :

时间复杂度 =n2+n3+n4……nn= \dfrac{n}{2} + \dfrac{n}{3} + \dfrac{n}{4} …… \dfrac{n}{n}=2n+3n+4n……nn

∵\because n2⩽n2,n3+n4⩽n2,n5+n6+n7+n8⩽n2……\dfrac{n}{2} \leqslant \dfrac{n}{2}, \dfrac{n}{3} + \dfrac{n}{4} \leqslant \dfrac{n}{2},\dfrac{n}{5} + \dfrac{n}{6} + \dfrac{n}{7} + \dfrac{n}{8} \leqslant \dfrac{n}{2} ……2n2n,3n+4n2n,5n+6n+7n+8n2n……

那么,一共有 log⁡2n\log_2 nlog2n 个这样的不等式相加

即:时间复杂度 ⩽log⁡2n⋅n2\leqslant \log_2 n \cdot \dfrac{n}{2}log2n2n

∴\therefore 时间复杂度 ⩽log⁡2n⋅n\leqslant \log_2 n \cdot nlog2nn

所以,我证明出来时间复杂度应该是 ⩽log⁡2n⋅n\leqslant \log_2 n \cdot nlog2nn 的。

而如果是 2−n2 - \sqrt n2n 的话,它的时间复杂度是 ⩽log⁡2n⋅n\leqslant \log_2 \sqrt n \cdot nlog2nn的,实际上 log⁡2n\log_2 \sqrt nlog2nlog⁡2\log_2log2 nnn 的差别并不大,例如,当 n=2×107n = 2 \times 10^7n=2×107 时, log⁡2\log_2log2 n\sqrt nn ≈12,\approx 12,12 log⁡2\log_2log2 nnn ≈24\approx 2424,只是相差了 222 倍 。

⑤⑤ 实际用时

外层循环枚举 2−n2 - \sqrt n2n

TLE 1104ms 86分

image-20220902200700435.png

外层循环枚举 2−n2 - n2n

TLE 1191ms 86分

image-20220902201201594

2.埃氏筛(sievesievesieve ofofof EratosthenesEratosthenesEratosthenes

①① 原理

其实,埃氏筛就是在暴力筛的基础上多加了一个判断:如果这个数不是质数便跳过。

这样做有什么好处呢?

因为如果这个数不是质数,那么它就有其他的质因子,那么为了加快速度,我们要尽可能的保证一个数只被筛 111 次,要想保证这个,就要保证这个数尽可能被自己的最小质因子筛到,所以便可以跳过。

②② 具体例子举例

例如:筛 202020 以内的质数。

首先,将 111 表示不是质数,预先算出 n=4\sqrt n = 4n=4

1−n1 - n1n1234567891011121314151617181920
ps_is_prime10000000000000000000

第一次循环,i=2i = 2i=2

1−n1 - n1n1234567891011121314151617181920
ps_is_prime10010101010101010101

第二次循环,i=3i = 3i=3

1−n1 - n1n1234567891011121314151617181920
ps_is_prime10010101110101110101

第三次循环:这便是一个优化了,由于 444 不为质数,所以,这层循环直接跳过。

③③ 代码展示
#include <bits/stdc++.h>
using namespace std;
const int N = 2e7 + 5;
int n;
bool ai_is_prime[N];
void ai_get(int m) {
    ai_is_prime[1] = 1;
    int nn = m;
    for (int i = 2; i <= nn; i++) {
        if (ai_is_prime[i])
            continue;
        for (int j = 2; j * i <= m; j++) {
            ai_is_prime[i * j] = 1;
        }
    }
}
int main() {
    cin >> n;
    ai_get(n);
    return 0;
}
④④ 时间复杂度及证明

此部分摘自网上(本人觉得自身证明时间复杂度的能力不足,并且数学公式实在是太难打了)

image-20220902201703613

当然,实际时间复杂度是大于等于O(log⁡O(\logO(log log⁡\loglog n)n)n) 的。

⑤⑤ 实际用时

外层循环枚举 2−n2 - \sqrt n2n

AC 336ms 100分

image-20220902202422049

外层循环枚举 2−n2 - n2n

AC 505ms 100分

image-20220902202153418

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值