OI退役笔记-002:简单数论-欧拉筛

素数

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

埃拉托斯特尼筛法

基本原理:
当需要求某一区间 [2, n] 内的所有素数时,可以从2开始,对于当前素数p,将 p2 后所有 p 的倍数筛去。
每次找到下一个没有被筛到的数就是一个素数。
时间复杂度:O(n*lglgn)
空间复杂度:O(n)

图示:
埃拉托斯特尼筛法图示
需加语句、头文件:

#include <cstdio>
int n;			// [2, n] 区间
bool v[10010];	//标记
// v[i] = 0:该点是素数;
// v[i] = 1:该点非质数;
// v 数组声明在全局中,则其所有值都为 0,且所有数先默认为素数,再筛去合数。

实现代码:

scanf("%d", &n);
for (int i = 2; i <= n; ++i)
{
	if(0 == v[i])
	{
		printf("%d ", i);
		for (int j = i * i; j <= n; j += i)
			v[j] = 1;
	}
}

欧拉筛

有名的欧拉筛,我不知道谁发明的 ,知道他快就行了

核心思想:基于埃氏算法,但比埃式算法快很多。

埃氏算法类似最简单的素数筛,即先筛掉 2 的倍数,再筛掉 2 的下一个未被筛掉的数(即:3)的倍数,以此类推(5、7……的倍数)。
埃式算法的盲点就是很多合数会被扫描不止一次,如 42:它会被 2、3、7各自筛一遍。所以效率相对较低,时间复杂度为:O(NlogNlogN)

于是,欧拉筛在扫描点上做了处理,每次扫描的数绝对都是没被扫过的。

核心思想:

每次得到素数时,向下筛一遍,确保每次用且只用某数的最小质因数进行筛选。具体如下:

  • 外层循环为 1→n,若 v[i] == 0,则表明其为质数(从未被筛过)。然后将其存放至 prime 数组中,写作
    prime[++cnt] = i。然后内层从 1→cnt,若 i * prime[j] <= n,则筛去该数(该数一定未被筛过且为合数)。
  • 接着,我们要使每个数只被其最小质因子筛去,则加入一条语句:如果 i % j == 0,则退出内层循环。

简单的正确性证明:
设一需要筛掉的合数 C 的最小质因数是 P,令 B = C / P(C=B × P),则 B 的最小质因数不小于 P(否则 C 的最小质因子不是 P)。那么当外层枚举到 i = B 时,便需要从小到大枚举素数;因为 i = B 的最小质因数不小于 P,所以 i 在质数枚举至 P 之前一定不会 break。于是,C 便会被 B × Pi 删去。

核心中的核心:恶心的 B 的最小质因数必不小于 P。

需加语句、头文件:

#include <iostream>
int prime[10010];
bool v[10010];
int cnt = 0;

欧拉筛代码:

scanf("%d", &n);
for (int i = 2; i <= n; ++i)
{
	if(v[i] == 0)
		prime[++cnt] = i;
	for(int j = 1; j <= cnt && i * prime[j] <= n; ++j)
	{
		v[i * prime[j]] = 1;
		if(i % prime[j] == 0)
			break;
	}
}

作者:Rotch
日期:2020-09-25
修改:2020-09-25

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页