欧拉筛质数以及四大积性数论函数(欧拉函数、莫比乌斯函数、约数个数函数、约数和函数)

先上板子,方便整理板子的时候使用它。后面再讲为什么。

#include<cstdio>
using namespace std;
#define IL inline 
typedef long long LL;
const int N = 1e6 + 3;
bool np[N];
int pri[N],phi[N],mu[N],di[N],si[N];
IL int Euler_sieve(int n) {
	int tot = 0;
	phi[0] = mu[0] = di[0] = si[0] = 0;
	np[1] = phi[1] = mu[1] = di[1] = si[1] = 1;
	for(int i=2;i<=n;i++) {
		if(!np[i]) {
			pri[++tot] = i;
			phi[i] = i-1;
			mu[i] = -1;
			di[i] = 2;
			si[i] = i + 1;
		}
		for(int j=1;j<=tot&&i*pri[j]<=n;j++) {
			np[i*pri[j]] = true;
			if(i % pri[j] == 0) {
				phi[i*pri[j]] = pri[j] * phi[i];
				mu[i*pri[j]] = 0;
				di[i*pri[j]] = (di[i] << 1) - di[i/pri[j]];
				si[i*pri[j]] = si[i] + pri[j] * (si[i]-si[i/pri[j]]);
				break;
			}
			phi[i*pri[j]] = phi[i] * (pri[j]-1);
			mu[i*pri[j]] = -mu[i];
			di[i*pri[j]] = di[i] << 1;
			si[i*pri[j]] = si[i] * (1+pri[j]);
		}
	}
	return tot;
}

变量名称解释:

  • tot:质数个数,函数Euler_sieve(int n)也会返回质数个数。
  • pri[i]:意为prime,这个数组里存储小于n的质数。
  • np[i]:意为not prime。这个bool数组是判断i是否为质数的。
  • phi[i]:即 φi。即为i的欧拉函数
  • mu[i]:即 μi。即为i的莫比乌斯函数
  • di[i]:意为divisor。即为i的约数个数函数
  • si[i]:即 σi。即为i的约数和函数

输入:n,意义见输出。
输出:小于等于n的所有质数以及质数个数。小于等于n的所有自然数的欧拉函数、莫比乌斯函数、约数个数函数、约数和函数。

建议已经学会了Eratosthenes筛法的同学继续往下读。#

什么是欧拉筛?欧拉筛的原理是什么?

欧拉筛是对于Eratosthenes筛法的优化。Eratosthenes筛法中由于重复筛了合数许多次,导致了它的复杂度是O(nloglogn)。那么欧拉筛的优化是对每个合数而言,只会用这个合数的最小质因子筛这个合数一次,这样复杂度就优化到了O(n),你就可以线性时间筛出质数以及各种数论函数了。


接下来我们分三个部分去理解欧拉筛。第一个部分是欧拉筛是怎么筛质数的,第二个部分是积性数论函数是什么,第三个部分是欧拉筛是怎么推出积性数论函数的。

欧拉筛是怎么筛质数的?#

欧拉筛是在Eratosthenes筛法基础上,利用每个正整数的最小质因数来筛质数的。显然可知,每个正整数的最小质因数有且只有一个,所以在欧拉筛的算法中,每个正整数只会被筛到一遍。在代码中的体现是,枚举当前的数字i,用这个数字i去筛它的倍数。在枚举i的循环中再枚举小于i的质数primejiprimej显然是合数,我们遍历到直接在np数组里打标记就好。如果此时iprimej的倍数,也即是说primeji的最小质因数,那么iprimej的最小质因数也显然是primej,此时就不必再枚举质数了,直接break出去。为什么这个时候要break出去呢?因为此时你找到了i的最小质因数primej,而如果你接着遍历质数的话,就不是用最小质因数去筛质数,也无法保证算法的复杂度是严格线性的。

积性数论函数是什么? (这段照抄oiwiki,原网址:https://oi-wiki.org/math/mobius/#

定义

xyf(xy)=f(x)f(y) ,则f(n)为积性函数。(xygcd(x,y)==1

性质

f(x)g(x)都为积性函数,则以下函数也为积性函数。

h(x)=f(xp)

h(x)=fp(x)

h(x)=f(x)g(x)

h(x)=d|xf(d)g(xd)

例子
  • 单位函数:ϵ(n)=1[n=1]
  • 恒等函数:idk(n)=nkid1(n)通常简记作id(n)
  • 常数函数:1(n)=1
  • 除数函数:σk(n)=d|ndkσ0(n)通常简记作d(n)τ(n),这是约数个数函数。σ1(n)通常简记作σ(n),这是约数和函数。
  • 欧拉函数:φ(n)=i=1n[gcd(i,n)=1]
  • 莫比乌斯函数:

μ(n)={1n=10d:d2|n(1)ω(n)otherwise

其中ω(n)表示n的不同质因子个数,是一个加性函数。


欧拉筛是怎么推出积性数论函数的?#

其实都是基于使用最小质因子去筛合数的条件去推的。可以发现,在欧拉筛中有三种情况,第一种是i这个数自己本身就是质数,第二种i不是primej的倍数,第三种iprimej的倍数。我们在接下来的欧拉筛筛函数过程中,都会分这三种情况讨论。
接下来的这个条件,在接下来的推法中都会用到。设一个数n,它的唯一分解式为:$$n=p_1{\alpha_1}p_2...p_s^{\alpha_s}$$
由于欧拉筛是用每个数的最小质因子去筛它,所以显然的,在primej|iprimej=p1
众所周知,这里每一个积性数论函数都有它自己通过自己质因子推出来的公式,我就不证这个公式直接用了。(逃

欧拉函数

众所周知,欧拉函数的公式是

φ(n)=ni=1s(11pi)

我们要通过这个公式,推导出在欧拉筛中φ(n)函数的筛法。

  • 第一种情况,i这个数自己本身就是质数时。φ(i)=n1
  • 第二种情况,i不是p1的倍数,说明ip1。由于欧拉函数是积性函数,由积性函数定义可得φ(ip1)=φ(i)φ(p1)=φ(i)(p11)
  • 第三种情况,ip1的倍数。观察它原来的公式,你发现φ(ip1)φ(i)=ip1i=p1,于是你得到了关系φ(ip1)=φ(i)p1
莫比乌斯函数

众所周知,莫比乌斯函数的公式是

μ(n)={1n=10d:d2|n(1)ω(n)otherwise

其中ω(n)表示n的不同质因子个数,是一个加性函数。
我们要通过这个公式,推导出在欧拉筛中μ(n)函数的筛法。

  • 第一种情况,i这个数自己本身就是质数时。μ(i)=1
  • 第二种情况,i不是p1的倍数,说明ip1。由于莫比乌斯函数是积性函数,由积性函数定义可得μ(ip1)=μ(i)μ(p1)=μ(i)
  • 第三种情况,ip1的倍数。这种情况显然在原公式中d:d2|n情况中。显然μ(ip1)=0
约数个数函数

众所周知,约数个数函数的公式是

d(n)=(1+α1)(1+α2)...(1+αs)

我们要通过这个公式,推导出在欧拉筛中d(n)函数的筛法。

  • 第一种情况,i这个数自己本身就是质数时。d(i)=2
  • 第二种情况,i不是p1的倍数,说明ip1。由于约数个数函数是积性函数,由积性函数定义可得d(ip1)=d(i)d(p1)=2d(i)
  • 第三种情况,ip1的倍数。利用原公式推导一下:

d(ip1)=(1+α1+1)(1+α2)...(1+αs)=(2+α1)(1+α2)...(1+αs)

=2(1+α1)(1+α2)...(1+αs)(1+α11)(1+α2)...(1+αs)=2d(i)d(ip1)

约数和函数

众所周知,约数和函数的公式是

σ(n)=(1+p1+p12+...+p1α1)(1+p2+p22+...+p2α2)...(1+ps+ps2+...+psαs)

我们要通过这个公式,推导出在欧拉筛中σ(n)函数的筛法。

  • 第一种情况,i这个数自己本身就是质数时。σ(i)=i+1
  • 第二种情况,i不是p1的倍数,说明ip1。由于约数和函数是积性函数,由积性函数定义可得 σ(ip1)=σ(i)σ(p1)=σ(i)(p1+1)
  • 第三种情况,ip1的倍数。利用原公式推导一下:

R=σ(i)(1+...+p1α1)=(1+...+p2α2)...(1+...+psαs)

A=(1+...+p1α11)(1+...+p2α2)...(1+...+psαs)=(1+...+p1α11)R=σ(ip1)

B=(1+...+p1α1)(1+...+p2α2)...(1+...+psαs)=(1+...+p1α1)R=σ(i)

C=(1+...+p1α1+1)(1+...+p2α2)...(1+...+psαs)=(1+...+p1α1+1)R=σ(ip1)

由此可以推出下面两个式子

p1A+R=B

p1B+R=C

p1σ(ip1)+R=σ(i)

p1σ(i)+R=σ(ip1)

由此得到

σ(ip1)=C=p1(p1A+R)+R=p12A+R(p1+1)=p12A+(Bp1A)(p1+1)=p1(BA)+B=p1(σ(i)σ(ip1))+σ(i)

于是,这样我熬夜爆肝终于写完了欧拉筛这四个积性函数的办法及证明。
我看别人可以有把约数个数函数不依靠其他辅助数组自己递推的做法,于是我想了想,约数和函数有没有自己递推的做法呢?于是这样我把它推了出来。为了纪念这个事情,特意熬夜爆肝写了一个巨长无比的blog。
以上。

posted @   bringlu  阅读(1202)  评论(0编辑  收藏  举报
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示
主题色彩