数论 · 质数

数论 · 质数

素数又称质数,是基础数论中的重要内容。

该文章主要分为三个部分:

质数定理

算法模板及时间复杂度分析

例题

1. 质数定理

1.1 质数除\(1\)和他本身之外没有其他因数

1.2 \(2\)是唯一一个偶数质数, 同时也是最小的质数, \(1\)不是质数

1.3 除了 \(1\) 之外的任何一个数不是质数就是合数(很明显, 却很常用)

1.4 能整除一个合数的最小因子一定小于等于\(\sqrt{n}\), 换言之, 如果一个数不能被\(2 - \sqrt{n}\)之间的任何一个数整除,那么这个数是质数

2. 代码模板及时间复杂度分析

2.1 试除法判定素数

上面已经说过, 如果一个数不能被\(2 - \sqrt{n}\)之间的任何一个数整除,那么这个数是质数

所以只要遍历 \(2 - \sqrt{n}\) 之间的所有数,判断能否整除 \(n\) 即可

时间复杂度最坏可以达到 \(O(\sqrt{n})\)

bool is_prime(int n)

{

if (n <= 1) return false; // 1并不是质数

for (int i = 2; i <= n / i; i ++ ) // 遍历从2 - sqrt(n) 之间的所有

if (n % i == 0) // 判断是否能整除n

return false;

return true;

}

2.2 筛法

筛法的作用就是筛出从 \(1 - n\) 之间的所有质数

筛法主要有两种

2.2.1 埃氏筛法

思路:遍历所有从 \(2 - n\) 之间所有数, 若数\(i\)是质数, 则将\(i - n\)之间所有能被\(i\)整除的标记为合数

Q: 为什么只标记质数的倍数就可以了呢??

A: 根据唯一分解定理: 每个\(>= 2\)的数的质因数分解形式只有一种, 且一定可以被质因数分解, 所以我们只需要考虑质数的倍数,就可以标记所有的合数

复杂度分析:

首先要遍历 \(2 - n\), 复杂度为 \(O(n)\)

其次要标记所有合数, 复杂度为 \(O(\frac{n}{p1} + \frac{n}{p2} ... )\)

而$\frac{n}{1} + \frac{n}{p2} ... $ 可以写成 \(n(\frac{1}{p1} + \frac{1}{p2} ... )\)

$\frac{1}{1} + \frac{1}{2} ... $ 被称为调和级数,大小约为 \(logn\)

而我们只对其中的素数进行向后的筛选,所以总复杂度为\(O(loglogn)\)

总时间复杂度为 \(O(nloglogn)\), 约为线性的复杂度

void get_primes(int n)

{

for (int i = 2; i <= n; i ++ )

{

if (!st[i]) primes[ ++ cnt] = i;

else continue;

for (int j = i + i; j <= n; j += i)

st[j] = true;

}

}

2.2.2 欧拉筛法

这是数学中使用频率最高的筛法, 思路同埃氏筛法, 不同的我们标记合数的时候, 是用前面已经得到的素数去更新后面的

void get_primes(int n)

{

for (int i = 2; i <= n; i ++ )

{

if (!st[i]) primes[ ++ cnt] = i; // 如果这个数没有被标记为合数,则为质数

for (int j = 1; primes[j] * i <= n; j ++ ) // 枚举前面已经得到的质数去更新后面的,同时要保证 primes[j] * i <= n, 因为更新大于n的数就没有意义了

{

st[i * primes[j]] = true;

if (i % primes[j] == 0) break; // 小优化

/*

1. 当 i % primes[j] != 0时, 说明此时遍历到的 primes[j] 不是 i 的质因子,那么只可能是此时的 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 的最小质因子,此时就应该

退出循环,避免之后重复进行筛选。

*/

}

}

}

时间复杂度为 \(O(n)\), 是线性复杂度

欧拉筛的代码稍有复杂,但是速度更快(但实际上个人在实际问题中,更喜欢写埃氏筛,因为比较好些啦 ~ )

3. 例题

Luogu P3383 线性筛模板

这道题非常讨厌,只能写欧拉筛才能过去(www写不了埃氏筛了)

#include

#include

#include

#include

using namespace std;

const int N = 1e8 + 10;

bool st[N];

int primes[N], cnt;

int n, q, x;

void get_primes(int n)

{

for (int i = 2; i <= n; i ++ )

{

if (!st[i]) primes[ ++ cnt] = i;

for (int j = 1; primes[j] * i <= n; j ++ )

{

st[i * primes[j]] = true;

if (i % primes[j] == 0) break;

}

}

}

int main()

{

scanf("%d%d", &n, &q);

get_primes(n);

while (q -- )

{

int x;

scanf("%d", &x);

printf("%d\n", primes[x]);

}

return 0;

}

相关推荐

DNF:如何把握打团时间?多号真惨,玩家:比上班还忙
1尺3幾寸:釐清傳統度量衡與現代公制換算,究竟是多少公分?
格兰蒂雅CRENTIA
mobileBET365

格兰蒂雅CRENTIA

📅 01-19 👁️ 3880
手机验机方法大全,如何完美避坑?
365bet手机端

手机验机方法大全,如何完美避坑?

📅 08-23 👁️ 4027
动漫大厦
365bet备用网

动漫大厦

📅 02-18 👁️ 9302
传奇世界哪里尸王多一些?探秘骷髅洞的秘密
365bet备用网

传奇世界哪里尸王多一些?探秘骷髅洞的秘密

📅 09-20 👁️ 4311