OI退役笔记-002:数论(二)素数(欧拉筛)

素数

素数又称质数,是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
唯一分解定理:(下一章详细解释)
任何大于 1 的素数都能写作 N = p1cp2c2…pmcm 的形式。
素数分布定理:
对于正实数 x,定义 Π(x) 为不大于 x 的质数,则 Π(x) ≈ x / lnx
因此,第 n 个质数 P(n) ≈ nlnx

埃拉托斯特尼筛法

基本原理:
当需要求某一区间 [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。

千万要注意,v[1] = 1,因为 1 不是质数
我在学校第一次集训考试时忘记这个,错了一个点,扣掉了五分。导致我就比第二名高了 8 分(老凡尔赛了)

需加语句、头文件:

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

欧拉筛代码:

scanf("%d", &n);
v[1] = 1;
for (int i = 2; i <= n; ++i)
{
	if(v[i] == 0)
		prime[++cnt] = i;
	// 这个 for 循环是所有数都参与的
	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
[2021-05-03]:添加两个定理,增加一些解释

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值