初等数论 —— 数论函数
数论函数(也称算数函数)是指定义域为\(\mathbb{N}^*\)的函数,数论函数也可以视为一个数列。
积性函数
积性函数
在数论中,若函数\(f(n)\)满足\(f(1)=1\),且\(f(xy)=f(x)f(y)[x,y\in\mathbb{N}^*,\ gcd(x,y)=1]\),则\(f(n)\)为积性函数。
在数论中,若函数\(f(n)\)满足\(f(1)=1\),且\(f(xy)=f(x)f(y)[x,y\in\mathbb{N}^*]\),则\(f(n)\)为完全积性函数。
积性函数的性质
- 若\(f(x)\)和\(g(x)\)为积性函数,则下列函数也为积性函数:\[\begin{flalign}& h(x)=f(x^p)\\&h(x)=f^p(x)\\&h(x)=f(x)g(x)\\&h(x)=\sum_{d|x}f(d)g({x\over d})\end{flalign} \]
- 对正整数\(x\),设其唯一质因数分解为\(x=\prod p_i^{k_i}\),其中\(p_i\)是质数:
若\(F(x)\)为积性函数,则有\(F(x)=\prod F(p_i^{k_i})\);
若\(F(x)\)为完全积性函数,则有\(F(x)=\prod F(p_i^{k_i})=prod F(p_i)^{k_i}\)。
积性函数的例子
- 单位函数:\(𝜀(n)=[n=1]\)(完全积性)
- 恒等函数:\(id_k(n)=n^k\),\(id_1(n)\)通常记作\(id(n)\)(完全积性)
- 常数函数:\(1(n)=1\)(完全积性)
- 除数函数:\(\sigma_k(n)=\sum_{d|n}d^k\),\(\sigma_0(n)\)通常记作\(d(n)\)或\(𝜏(n)\),\(\sigma_1(n)\)通常记作\(\sigma(n)\)。
- 欧拉函数:\(\varphi(n)=\sum_{i=1}^n[(i, n)=1]\)。
- 莫比乌斯函数:\(\mu(n)=\begin{cases}1\quad\quad\quad\quad n=1\\0\quad\quad\quad\quad \exists d > 1,d^2|n\\(-1)^{w(n)}\quad otherwise\end{cases}\),其中\(𝜔(n)\)表示\(n\)质因数分解中不同质因子的个数。
加性函数
加性函数
在数论中,若函数\(f(x)\)满足\(f(1)=1\)且\(f(x+y)=f(x)+f(y)[x,y\in\mathbb{N}^*,\ gcd(x,y)=1]\),则\(f(x)\)为加性函数。
在数论中,若函数\(f(x)\)满足\(f(1)=1\)且\(f(x+y)=f(x)+f(y)[x,y\in\mathbb{N}^*]\),则\(f(x)\)为完全加性函数。
加性函数的性质
- 对于正整数\(x\),设其唯一质因数分解为\(x=\prod p_i^{k_i}\),其中\(p_i\)为质数:
若\(F(x)\)为加性函数,则有\(F(x)=\sum F(p_i^{k_i})\);
若\(F(x)\)为完全加性函数,则有\(F(x)=\sum F(p_i^{k_i})=\sum F(p_i)\times k_i\)。
加性函数的例子
设所有素数组成的集合为\(\mathbb{P}\)。
- 质因数分解中\(p\)的重数:\(𝜈_p(n)=max\{k\in\mathbb{N}:p^k|n\}\),其中\(p\in\mathbb{P}\);(完全加性)
- 所有质因子数目:\(Ω(n)=\sum_{p\in\mathbb{P}}𝜈_p(n)\);(完全加性)
- 相异质因子数目:\(𝜔(n)=\sum_{p\in\mathbb{P}}[p|n]\);
- 所有质因子之和:\(a_0(n)=\sum_{p\in\mathbb{P}}𝜈_p(n)\times p\);(完全加性)
- 相异质因子直和:\(a_1(n)=\sum_{p\in\mathbb{P}}[p|n]\times p\)。
线性筛在积性函数中的应用
线性筛求欧拉函数
线性筛中,每一个合数都会被最小的质因子筛掉,例如\(n=p_1\times n'\),其中\(p_1\)是\(n\)最小的质因子,那么线性筛会在第一层循环\(i=n'\)时,通过
isPrime[i * primes[j]] = false筛掉。
观察线性筛的过程,我们还需要处理两个部分,对\(n'\pmod{p_1}\)分类讨论:
- 若\(n'\equiv 0\pmod{p_1}\),那么\(n'\)包含了\(n\)的所有质因子:\[\begin{flalign}&\varphi(n)=n\times \prod_{i=1}^s{p_i-1\over p_i}\\&\quad\quad\ = p_1\times n'\times\prod_{i=1}^s{p_i-1\over p_i}\\&\quad\quad\ =p_1\times\varphi(n')\end{flalign} \]
- 若\(n'\not\equiv 0\pmod{p_1}\),这时\(n'\)与\(p_1\)互素,由欧拉函数积性函数的性质:\[\begin{flalign}&\varphi(n)=\varphi(p_1)\times \varphi(n')\\&\quad\quad\ = (p_1-1)\times \varphi(n')\end{flalign} \]
vector<int> primes;
vector<bool> isPrime(n + 1, true);
vector<int> phi(n + 1);
void getPhi(int n) {
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
primes.push_back(i);
phi[i] = i - 1;
}
for (int j = 0; i < primes.size() && i * primes[j] <= n; j++) {
isPrime[i * primes[j]] = false;
if (i % primes[j] == 0) {
// i % pj = 0
phi[i * primes[j]] = phi[i] * primes[j];
break;
} else {
// i % pj != 0
phi[i * primes[j]] = phi[i] * phi[primes[j]];
}
}
}
}
线性筛求莫比乌斯函数
由莫比乌斯函数的定义可知:
- 初始状态:\(\mu(1)=1\);
- 素数状态:如果\(p\)是素数,那么它只有一个质因子,即\(𝜔(p)=1\),即\(\mu(p)=(-1)^1=-1\);
- 平方因子状态:如果一个数包含某个质数平方作为因子,其函数值为\(0\)。
在线性筛中,我们用\(i\)乘以已知的质数\(p_j\)(即primes[j])来筛掉合数\(n=i\times p_j\)。由于\(p_j\)是从小到大遍历的,此时\(p_j\)一定是\(i\times p_j\)的最小质因子。我们对\(i\pmod{p}\)分情况讨论:
- \(i\not\equiv0\pmod{p_j}\)
这意味着\(i\)内部不包含质因子\(p_j\),即\(i\)与\(p_j\)互质,由莫比乌斯函数是积性函数的形式:\[\mu(i\times p_j)=\mu(i)\times\mu(p_j) \]又因为\(p_j\)是质数,已知\(\mu(p_j)=-1\),所以:\[\mu(i\times p_j)=\mu(i)\times (-1)=-\mu(i) \] - \(i\equiv 0\pmod{p}\)
这意味着\(p_j\)已经是\(i\)的一个质因子了,既然\(i\)中已经包含了一个\(p_j\),那么在构造出来的合数\(n=i\times p_j\)中,必然包含了\(p_j^2\)这个平方因子,因此:\[\mu(i\times p_j)=0 \]并且根据线性筛的规则,此时我们应该执行break退出内层循环,保证每个数只被其最小的质因子筛掉。
vector<int> primes;
vector<bool> isPrime(n + 1, true);
vector<int> mu(n + 1, 0);
void getMu(int n) {
isPrime[0] = isPrime[1] = false;
mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
primes.push_back(i);
mu[i] = -1;
}
for (int j = 0; j < primes.size() && i * primes[j] <= n; j++) {
isPrime[i * primes[j]] = false;
if (i % primes[j] == 0) {
mu[i * primes[j]] = 0;
break;
} else {
mu[i * primes[j]] = -mu[i];
}
}
}
}
线性筛求约数个数
约数可以视作定义在\(\mathbb{N}^*\)上的因数。
约数个数定理
对于正整数\(N=\prod_{i=1}^mp_i^{k_i}\),它的任何一个约数\(d\)必然也只能由这些质因子\(p_i\)组成,即:\[d=p_1^{a_1}\times p_2^{a_2}\times\cdots\times p_m^{a_m} \]其中对于每一个质因子的指数\(a_i\),它的取值范围是\(0\leq a_i\leq k_i\)。也就是说,\(a_i\)有\(k_i+1\)种选择(从\(0\)到\(k_i\))。
根据排列组合中的乘法定理,正整数\(N\)的约数个数(通常记作\(d(n)\)或\(\tau(n)\))为所有质因子指数加\(1\)后的连乘积:\[d(N)=(k_1+1)\times(k_2+1)\times\cdots\times(k_m+1)=\prod_{i=1}^m(k_i+1) \]
约数个数函数\(d(n)\)是一个积性函数。(除数函数的特例)
要理利用线性筛在\(O(n)\)的时间复杂度内求解出\([1,n]\)的约数个数,我们需要在筛的过程中维护好约数个数定理中的\((k_1+1)\)这一项。
在线性筛中,每个数\(n\)都会被它的最小质因子筛掉。为了推导状态转移,除了需要数组\(d\)记录约数个数以外,我们还需要引入一个辅助数组num(或e):
d[i]:表示整数\(i\)的约数个数;num[i]:表示整数\(i\)的最小质因子的指数(即唯一分解式中\(p_1\)的指数\(k_1\))。
- 初始状态:\(d(1)=1\);
- 素数状态:如果\(p\)是素数,它只有\(1\)和它本身两个约数,并且它的最小质因子就是它本身,指数为\(1\)。
d[p] = 2;num[p] = 1
- 状态转移:
对\(i\times p_j\)构造的合数进行\(i\pmod{p_j}\)分类讨论:
① \(i\not\equiv 0\pmod{p_j}\)
这意味着\(p_j\)不是\(i\)的质因子,即\(i\)与\(p_j\)互质。因为\(p_j\)是新加入的最小质因子,根据积性函数的性质以及约数个数定理:- \(i\times p_j\)相当于在\(i\)的基础上多了一个独立的质因子\(p_j^1\);
- 根据公式,\(d(i\times p_j)=d(i)\times d(p_j)=d(i)\times 2\);
- 此时对于新合数\(i\times p_j\)而言,它的最小质因子变成了\(p_j\),且指数为\(1\),所以
num[i * pj] = 1;
② \(i\equiv 0\pmod{p}\)
这意味着\(p_j\)已经是\(i\)的最小质因子了,设\(i\)的分解式为\(i=p_j^k\times m\),(其中\(k\)就是当前的num[i]),那么\(d(i)=(k+1)\times d(m)\)。
当计算\(i\times p_j\)时,分解式变成了\(p_j^{k+1}\times m\),根据约数个数定理,新的约数个数应该是\(d(i\times p_j)=(k+2)\times d(m)\)。
为了从已知的\(d(i)\)推导\(d(i\times p_j)\),我们可以先除以原来的贡献\((k+1)\),再乘上新的贡献\((k+2)\):
\[d(i\times p_j)={d(i)\over num[i]+1}\times(nums[i] + 2) \]同时,最小质因子的指数增加\(1\):\[nums[i\times p_j]=nums[i]+1 \]最后break跳出内层循环。
vector<int> primes;
vector<bool> isPrime(n + 1, true);
// d[i] 存储 i 的约数个数
vector<int> d(n + 1, 0);
// num[i] 存储最小质因子的指数
vector<int> num(n + 1, 0);
void getDivisorCount(int n) {
isPrime[0] = isPrime[1] = false;
d[1] = 1;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
primes.push_back(i);
d[i] = 2;
num[i] = 1;
}
for (int j = 0; j < primes.size() && i * primes[j] <= n; j++) {
isPrimes[i * primes[j]] = false;
if (i % primes[j] == 0) {
d[i * primes[j]] = d[i] / (num[i] + 1) * (num[i] + 2);
num[i * primes[j]] = num[i] + 1;
break;
} else {
d[i * primes[j]] = 2 * d[i];
num[i * primes[j]] = 1;
}
}
}
}
线性筛求约数和
约数之和定理
在数论中,约数之和(通常记作\(\sigma(n)\)或\(\sigma_1(n)\))表示正整数\(n\)的所有正约数的和。基于算术基本定理:\[N=p_1^{k_1}\times p_2^{k_2}\times\cdots\times p_m^{k_m}=\prod_{i=1}^mp_i^{k_i} \]约数之和定理指出,\(N\)的所有正约数之和的为个质因子的等比数列求和的乘积:
\[\sigma(N)=(1+p_1+p_1^2+\cdots+p_1^{k_1})\times(1+p_2+p_2^2+\cdots+p_2^{k_2})\times\cdots\times(1+p_m+p_m^2+\cdots+p_m^{k_m}) \]\[\sigma(N)=\prod_{i=1}^m\left(\sum_{j=0}^{k_j}p_i^j\right)=\prod_{i=1}^m{p_i^{k_i+1}-1\over p_i-1} \]
约数之和函数\(\sigma(n)\)是一个积性函数。在线性筛中求约数之和,核心在于维护上述公式中最小质因子的等比数列求和项。
我们引入两个辅助数组:
sigma[i]:表示整数\(i\)的约数之和;pSum[i]:表示整数\(i\)的最小质因子\(p_1\)对应的等比数列求和项(即\(1+p_1+p_1^2+\cdots+p_1^{k_1}\))
- 初始状态:\(\sigma(1)=1\);
- 素数状态:若\(p\)是素数,它的约数只有\(1\)和\(p\):
sigma[p] = p + 1pSum[p] = p + 1
- 状态转移
由\(i\times p_j\)构造的合数,通过\(i\pmod{p_j}\)进行分类讨论:- \(i\not\equiv0\pmod{p_j}\)
说明\(i\)与\(p_j\)互质,由积性函数的性质直接相乘即可,\(p_j\)作为新加入的最小质因子,其对应的等比数列项仅为\(1+p_j\)
sigma[i * pj] = sigma[i] * sigma[pj]
pSum[i * pj] = pSum[pj] - \(i\equiv0\pmod{p_j}\)
说明\(p_j\)是\(i\)的最小质因子,设\(i\)的分解式中最小质因子部分为\(p_j^k\),则pSum[i]记录的值为\(1+p_j+\cdots+p_j^k\)。计算\(i\times p_j\)时,\(p_j\)的指数变成了\(k+1\),此时的等比数列求和项应更新为:\[1+p_j+p_j^2+\cdots+p_j^k+p_j^{k+1} \]可观察到一个递推关系为:\[(1+p_j+p_j^2+\cdots+p_j^k)\times p_j+1=1+p_j+p_j^2+\cdots+p_j^k+p_j^{k+1} \]即:
pSum[i * pj] = pj * pSum[i] + 1
sigma[i * pj] = (sigma[i] / pSum[i]) * pSum[i * pj]
- \(i\not\equiv0\pmod{p_j}\)
vector<int> primes;
vector<bool> isPrime(n + 1, true);
vector<long long> sigma(n + 1, 0);
vector<long long> pNum(n + 1, 0);
void getDivisorSum(int n) {
isPrime[0] = isPrime[1] = false;
sigma[1] = 1;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
primes.push_back(i);
pNum[i] = 1 + i;
sigma[i] = 1 + i;
}
for (int j = 0; j < primes.size() && i * primes[j] <= n; j++) {
isPrime[i * primes[j]] = false;
if (i % primes[j] == 0) {
pSum[i * primes[j]] = primes[j] * pSum[i] + 1;
sigma[i * primes[j]] = (sigma[i] / pSum[i]) * pSum[i * primes[j]];
break;
} else {
pSum[i * primes[j]] = pSum[primes[j]];
sigma[i * primes[j]] = sigma[i] * sigma[primes[j]];
}
}
}
}
一般的积性函数
假设一个积性函数\(f\)满足:对于任意质数\(p\)和正整数\(k\),可以在关于\(k\)的低次多项式时间内计算\(f(p^k)\),那么就可以在\(O(n)\)的时间内筛出\(f(1),f(2),\cdots,f(n)\)的值。
设合数\(n\)的质因子分解是\(\prod_{i=1}^k p_i^{\alpha_i}\),其中\(p_1<p_2<\cdots<p_k\)为质数,在线性筛中记录\(g_n=p_1^{\alpha}\),假设\(n\)被\(x\times p\)筛掉(\(p\)是质数),那么\(g\)满足如下递推关系式:
假如\(n=g_n\),说明\(n\)就是某个质数的次幂,可以\(O(1)\)计算\(f(n)\);否则,\(f(n)=f({n\over g_n})\times f(g_n)\)。

数论函数(线性筛求解积性函数)
浙公网安备 33010602011771号