基础数论

基础数论

Update: 2024/11/02。

素数

素数和合数

定义

若 \(p \in \Zeta\),且 \(p \not= 0, \pm1\),其约数集合中的元素只有 \(1\) 和 \(p\) 本身,那么称 \(p\) 为素数。

若 \(a \in \Zeta\),且 \(a \not= 0, \pm1\), \(a\) 不为素数,则为合数。

素数一般指正的素数。

素数计数

\(\pi(x)\) 表示小于或等于 \(x\) 的素数个数。随 \(x\) 增大,近似结果:

\(\pi(x) \sim \frac{x}{\ln(x)}\)。

$ 10^9 $ 以内的数字,约数个数最多的数字的约数为 \(1536\) 个。

素数判定

试除。枚举 \(1 \sim \sqrt{n}\) 整数,看是否能够整除。

cpp 复制代码
bool isPrime(a) {
  if (a < 2) return 0;
  for (int i = 2; i * i <= a; ++i)
    if (a % i == 0) return 0;
  return 1;
}

素数筛法

埃氏筛

对于任意大于 \(1\) 的正整数 \(n\) ,那么它的 \(x\) 倍为合数\((x > 1)\)。

如果我们从小到大考虑每个数,然后同时把当前这个数的所有(比自己大的)倍数记为合数,那么运行结束的时候没有被标记的数就是素数了。

cpp 复制代码
vector<int> prime;
bool is_prime[N];

void Eratosthenes(int n) {
  is_prime[0] = is_prime[1] = false;
  for (int i = 2; i <= n; ++i) 
      is_prime[i] = true;
  int x = sqrt(n);
  for (int i = 2; i <= x; ++i) {
    if (is_prime[i])
      for (int j = i * i; j <= n; j += i) 
          is_prime[j] = false;
  }
  for (int i = 2; i <= n; ++i)
    if (is_prime[i]) 
        prime.push_back(i);
}

时间复杂度为 \(\Omicron(n \log \log n)\)。

线性筛

可以注意到, 埃氏筛法仍有优化空间,它会将一个合数重复多次标记。

让每个合数都只被标记一次即可。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e8+10;
const int maxm = 1e7;
int v[maxn],prime[maxm]; // v[i] 表示 i 的最小质因子
int n, q, cnt=0;

void is_prime() { // 线性筛
	memset(v, 0, sizeof(v));
	for(int i=2; i<=n; ++i) {
		if( !v[i] ) {
			v[i] = i;
			prime[++cnt] = i;
		}
		for(int j=1; j<=cnt; ++j) {
			if(prime[j] > v[i] || prime[j] > n/i)
				break;
			v[i*prime[j]] = prime[j];
		}
	}
}

int main() {
	scanf("%d%d", &n, &q);
	is_prime();
	while(q--) {
		int x;
		scanf("%d", &x);
		printf("%d\n", prime[x]);
	}
	return 0;
}

区间筛法

cpp 复制代码
using ll = long long;

bool isprime[maxn], primepart[maxn];

void func(ll l, ll r) {
	for (ll i = 0; i * i <= r; ++i) 
		primepart[i] = 1;
	primepart[1] = 0;
	for (int i = 0; i <= r - l; ++i)
		isprime[i] = 1;
	for (ll i = 2; i * i <= r; ++i) {
		if (primepart[i]) {
			for (ll j = i * i; j * j <= r; j += i)
				primepart[j] = 0;
			for (ll j = max(2ll, (l + i - 1) / i) * i; j <= r; j += i)
				isprime[j - l] = 0;
		}
	}
	int ans = 0;
	for (ll i = l; i <= r; ++i)
		if (isprime[i - l] && i != 1)
			ans++;
	printf("%d\n", ans);
	return;
}

相关题目

  1. 素数密度

质因子分解

由唯一分解定理得。

cpp 复制代码
void divide(int n) {
	m = 0;
	int  t = sqrt(n);
	for(int i=2; i<=t; ++i) {
		if(n % i == 0) {  // 此处能整除n的i一定是质数
			p[++m] = i;	c[m] = 0;
			while(n % i == 0) {
				n /= i;	c[m]++;
			} 
		}
	}
	if(n > 1) {  // 原始的n是质数或者原始的n中最大的质因子大于 sqrt(n)
		p[++m] = n;	c[m] = 1; 
	}
}

约数

定义

若 $n \in $ \(\Zeta\),$d \in $ $ \Zeta $, \(n\) \(\%\) \(d\) \(=\) \(0\),则称 \(d\) 是 \(n\) 的约数,\(n\) 是 \(d\) 的倍数,记作 $ d \mid n $。

算数基本定理

引理

设 \(p\) 是素数,\(p | a_1a_2\),那么 \(p | a_1\) 和 \(p|a_2\) 至少有一个成立。

唯一分解定理

设有 \(a \in \Nu^*\),那么其一定可以表示为若干个素数的乘积。

标准素因数分解式

设有 \(a \in \Nu^*\),必有:

\(a=p_1^{c_1}p_2^{c_2}...p_m^{c_m}, p_1 < p_2 <...< p_m\)

称为正整数 \(a\) 的标准素因数分解式。

算数基本定理与算数基本引理,两个定理等价。

对 n! 质因子分解

考虑到 \(1\sim n\) 中一个质数 \(p\) 出现次数为 \(\lfloor \frac{n}{p} \rfloor +\lfloor \frac{n}{p^2} \rfloor+\lfloor \frac{n}{p^3} \rfloor \dots \lfloor \frac{n}{p^c} \rfloor\)。

可在求大数组合数时使用。

cpp 复制代码
int get_sum(int x, int y) {
	int sum = 0;
	while (x) {
		sum += x / y;
		x /= y;
	}
	return sum;
}

算数基本定理的推论

在算数基本定理中,若 \(n \in \Nu^*\) 被唯一分解为 \(n = p_1^{c_1}p_2^{c_2}...p_m^{c_m}\),其中 \(c_i \in \Nu^*\),\(p_i\) 都是质数,且满足 \(p_1 < p_2 < ...<p_m\),则 \(n\) 的正约数集合可写作:

{\(p_1^{b_1}p_2^{b_2}...p_m^{b_m}\)} ,\(0 \leq b_i \leq c_i\)

\(n\) 的正约数个数为:

\(\prod\limits^{m}{i=1}\big( \sum\limits^{c_i}{j=0}(p_i)^j \big)\)

求\(N\) 的正约数集合------试除法

若 \(d \geq \sqrt{N}\) 是 \(N\) 的约数,则 $ d \mid N$ 也是 \(N\) 的约数。也就是说,除完全平方数外,约数总是成对出现的。

据此可得:

cpp 复制代码
vector<int> res;
int n;

int main(){
   cin >> n;
   
   for(int i = 1; i <= n / i; i ++)
   	if(n % i == 0){
   		res.push_back(i);
   		if(i < n / i)
   			res.push_back(n / i);
   	}
   
   for(auto t : res) cout << t << ' ';
   
   return 0;
}

时间复杂度为 \(\Omicron(\sqrt{N})\)。

试除法的推论

对于一个 \(n \in \Zeta\),其约数个数的上界为 \(2 \sqrt{n}\)。

求 \(1 \sim N\) 每个数的正约数集合------倍数法

cpp 复制代码
vector<int> factor[500010];
inline void bei(int n){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n/i;j++)
            factor[i*j].push_back(i);
    for(int i=1;i<=n;i++){
        for(int j=0;j<factor[i].size();j++)
            printf("%d ",factor[i][j]);
        puts("");
    }
}

时间复杂度为 \(\Omicron(N +N/2+N/3+...+N/N)=\Omicron(N\log{N})\)。

倍数法的推论

\(1 \sim N\) 每个数的约数个数总和大约为 \(N\log{N}\)。

例题

  1. [POI2001] [HAOI2007] 反素数

最大公约数

定义

若自然数 \(d\) 同时是自然数 \(a\) 和 \(b\) 的约数,则称 \(d\) 是 \(a\) 和 \(b\) 的公约数。

在所有 \(a\) 和 \(b\) 的公约数中最大的一个数称为 \(a\) 和 \(b\) 的最大公约数。记作 \(\gcd(a, b)\)。

若自然数 \(d\) 同时是自然数 \(a\) 和 \(b\) 的倍数,则称 \(d\) 是 \(a\) 和 \(b\) 的公倍数。

在所有 \(a\) 和 \(b\) 的公倍数中最大的一个数称为 \(a\) 和 \(b\) 的最大公倍数。记作 \(\text{lcm}(a, b)\)。

同理即可定义多个数的最大公约数与最大公倍数。

定理

\(\forall a,b \in \Nu, \gcd(a,b)*\text{lcm}(a,b)=a*b\)。

九章算术·更相减损术

\(\forall a, b \in \Nu,a \geq b\),有 \(\gcd(a, b) = \gcd(b, a - b) = \gcd(a, a - b)\)。

\(\forall a, b \in \Nu\),有 \(\gcd(2a, 2b) = 2\gcd(a, b)\)。

欧几里德算法

\(\forall a, b \in \Nu, b \not= 0, \gcd(a,b) = \gcd(b, a \% b)\)。

由此可得求 \(\gcd\) 的代码实现:

cpp 复制代码
int gcd(int a, int b) {
	return b ? gcd(b, a%b) : a;
}

时间复杂度为 \(\Omicron(\log(a+b))\)。

由于高精除法(取模)不容易实现,可考虑用更相减损术代替。

例题

  1. gcd与lcm
  2. Neko does Maths

互质

定义

\(\forall a, b \in \Nu\),若 \(\gcd(a, b)=1\),则称 \(a,b\) 互质,记作 \(a \bot b\)。

对于多个数字,\(\gcd(a,b,c)=1\) 称为 \(a,b,c\) 互质。把 \(\gcd(a,b)=\gcd(a,c)=\gcd(b,c)=1\) 称为 \(a, b, c\) 两两互质。

积性函数

定义

若函数 \(f(x)\) 满足 \(f(1)=1\) 且 \(\forall a, b \in \Nu^*, a \bot b\),都有 \(f(ab)=f(a)f(b)\),则称 \(f(x)\) 为积性函数。

若函数 \(f(x)\) 满足 \(f(1)=1\) 且 \(\forall a, b \in \Nu^*\),都有 \(f(ab)=f(a)f(b)\),则称 \(f(x)\) 为完全积性函数。

欧拉函数

定义

欧拉函数(Euler's totient function ),即 \(\varphi(x)\),表示小于等于 \(n\) 的数字中与 \(n\) 互质的的数字个数。

欧拉函数为积性函数。

性质

  1. 根据唯一分解定理,设 \(n=\prod\limits^{m}{i=1}p_i^{c_i}\),则 $\varphi(n)=n \times \prod\limits^{m}{i=1}(1-\frac{1}{p_i}) $。

证明

若 \(n\) 的质因子个数为 \(2\),为 \(p_1,p_2\)。

设一个数的质因子集合为 \(\mathbb{A}\),另一个数的质因子集合为 \(\mathbb{B}\),那么若两数互质,则必满足 \(\mathbb{A} \cap \mathbb{B} = \phi\)。

所以若一个数与 \(n\) 互质,则它的质因子集合中一定不包含 \(p_1,p_2\)。

所以 \(1 \sim n\) 中与 \(n\) 互质的数字一定不是 \(p_1, p_2\) 的倍数。

因此 \(1 \sim n\) 中与 \(n\) 互质的数字个数为 \(n-\frac{n}{p_1}-\frac{n}{p_2}+\frac{n}{p_1p_2}\)。

之所以需要加上一个 \(\frac{1}{p_1p_2}\),是因为在去除 \(n\) 中 \(p_1,p_2\) 倍数时多去除了一遍 \(n\) 中 \(p_1p_2\) 的倍数,需要再加回来。

也就是二元容斥原理。

当 \(n\) 的质因子个数大于 \(2\) 时,则涉及多元容斥原理。

\(n-\frac{n}{p_1}-\frac{n}{p_2}+\frac{n}{p_1p_2}=n(1-\frac{1}{p_1} )(1-\frac{1}{p_2} )\)。

所以此时 \(\varphi(n)=n(1-\frac{1}{p_1} )(1-\frac{1}{p_2})\)。

同理可得,当 \(n\) 的质因子个数为 \(3\),为 \(p_1,p_2,p_3\) 时,

\(\varphi(n)=n(1-\frac{1}{p_1} )(1-\frac{1}{p_2})(1-\frac{1}{p_3})\)。

归纳可得,若 \(n=\prod\limits^{m}{i=1}p_i^{c_i}\),则 \(\varphi(n)=n \times \prod\limits^{m}{i=1}(1-\frac{1}{p_i})\)。

证毕.

  1. \(1 \sim n\) 中与 \(n\) 互质的数字和为 $\frac{n}{2} \varphi(n) $。

证明

在 \(1\sim n\) 中若有一个数字 \(x\),\(x \bot n\),则 \(\gcd(x,n)=1\),

由更相减损术可得,\(\gcd(n,n-x)=1\),即 \((n-x)\bot n\),且 \((n-x)\in [1,n]\)。

\(n,n-x\) 的平均数为 $\frac{n}{2} $。

所以可知在 \([1,n]\) 中,与 \(n\) 互质的数字总成对存在,且这些数字平均数为 $\frac{n}{2} $。

又因为在 \([1,n]\) 中与 \(n\) 互质的数字个数为 \(\varphi(n)\),

所以 \(1 \sim n\) 中与 \(n\) 互质的数字和为 $\frac{n}{2} \varphi(n) $。

证毕.

  1. \(\varphi(x)\) 为积性函数,即 \(\forall a,b\) 且 \(a\bot b\), 都有 $ \varphi(ab)=\varphi(a)\varphi(b)$。

证明

设 \(a=\prod\limits^{x}{i=1}p_i^{c_i},b=\prod\limits^{y}{i=1}q_i^{d_i}\),且 $a \bot b $,则 \(\varphi(a)=a\times\prod\limits^{x}{i=1}(1-\frac{1}{p_i}),\varphi(b)=b\times\prod\limits^{y}{i=1}(1-\frac{1}{q_i})\)。

那么 \(ab=\prod\limits^{x}{i=1}p_i^{c_i}\times\prod\limits^{y}{i=1}q_i^{d_i}\),则 \(\varphi(ab)=ab\times\prod\limits^{x}{i=1}(1-\frac{1}{p_i})\times\prod\limits^{y}{i=1}(1-\frac{1}{q_i})\)。

所以 \(\varphi(ab)=a\times b\times\prod\limits^{x}{i=1}(1-\frac{1}{p_i})\times\prod\limits^{y}{i=1}(1-\frac{1}{q_i})=\varphi(a)\varphi(b)\),即 \(\varphi(x)\) 为积性函数。

证毕.

  1. 当 \(n=p^k\),\(p\) 为质数,则 \(\varphi(n) = (p-1)*p^{k-1}\),且 \(\varphi(p)=p-1\)。

证明

\(1 \sim n\) 中与 \(n\) 不互质的数字均为 \(p\) 的倍数,而 \(1 \sim n\) 中 \(p\) 的倍数有 \(p^{k-1}\) 个,故 \(\varphi(n)=p^k-p^{k-1}=(p-1)*p^{k-1}\)。

证毕.

  1. 若 \(p\) 为质数,且 \(p\) 为 \(n\) 的约数,那么有 \(\varphi(n*p)=\varphi(n)*p\)。

原因显然。

另一种说法

若 \(p\) 为质数,\(p | n\) 且 \(p^2|n\),则 \(\varphi(n) = \varphi(\frac{n}{p}) * p\)。

  1. 若 \(p\) 为质数且 \(p\) 不为 \(n\) 的约数,那么有 \(\varphi(n*p)=\varphi(n)*(p-1)\)。

证明

\(\gcd(n,p)=1\),即 \(n,p\) 互质,那么 \(\varphi(n*p)=\varphi(n)*\varphi(p)=\varphi(n)*(p-1)\)。

证毕.

  1. \(\sum\limits^{}_{d|n} \varphi(d)=n。\)

证明

设有函数 \(f(n) = \sum\limits^{}{d|n} \varphi(d)\),若有 \(n, m\) 互质,则 \(f(nm) = \sum\limits^{}{d|nm} \varphi(d)=\sum\limits^{}{x|n} \sum\limits^{}{y|m} \varphi(xy)=\sum\limits^{}{x|n} \varphi(x) * \sum\limits^{}{y|m} \varphi(y)=f(n)f(m)\),即 \(f(n)\) 为积性函数。

对于单个质因子 \(p^m\),有 \(f(p^m)=\sum\limits^{}_{d|{p^m}} \varphi(d)=\varphi(1)+\varphi(p)+\varphi(p^2)+...+\varphi(p^m)\)。可知这是个等比数列求和再加一,结果为 \(p^m\)。

所以 \(f(n)=\prod\limits^{m}{i=1} f(p_i^{c_i})=\prod\limits^{m}{i=1} p_i^{c_i}=n\)。

证毕.

求欧拉函数值

  1. 求单个值 \(n\) 的欧拉函数值:
cpp 复制代码
int phi(int n) {
	int ans = n;
	int t = sqrt(n);
	for(int i=2; i<=t; ++i) {
		if(n%i == 0)
			ans = ans/i*(i-1);
		while(n%i == 0) n /= i;
	}
	if(n > 1) ans = ans/n/(n-1);
	return ans;
}

时间复杂度为 \(\Omicron(\sqrt{n})\)。

  1. 埃氏筛求 \(1 \sim n\) 的欧拉函数值:
cpp 复制代码
void found_euler(int n) {
	for(int i=1; i<=n; ++i) phi[i] = i;
	for(int i=2; i<=n; ++i) {
		if(phi[i] == i) { // i为质数 
			for(int j=i; j<=n; j+=i) // 给包含质因子i的数字,乘上 (1-1/i) 
				phi[j] = phi[j]/i*(i-1); 
		}
	}
} 

时间复杂度为 \(\Omicron(n \log n)\)。

  1. 线性筛求 \(1 \sim n\) 的欧拉函数值:
cpp 复制代码
int v[maxn], p[maxn], phi[maxn];

void found_euler(int n) {
	memset(v, 0, sizeof(v));
	cnt = 0;
   phi[1] = 1; 
	for(int i=2; i<=n; ++i) {
		if(v[i]==0) {
			v[i] = i;
			p[++cnt] = i;
			phi[i] = i-1; // i是质数 
		}
		for(int j=1; j<=cnt; ++j) {
			if(p[j]>v[i] || p[j]>n/i)
				break;
			v[i*p[j]] = p[j];
			if(i%p[j] == 0) // i*p[j]是合数,要么互质,要么 p[j] 是 i的最小质因子(约数) 
				phi[i*p[j]] = phi[i] * p[j];
			else phi[i*p[j]] = phi[i] * (p[j]-1);
		}
	}
}

时间复杂度为 \(\Omicron(n)\)。

同余

定义

若整数 \(a, b\) 除以正整数 \(m\) 的余数相等,则称 \(a,b\) 模 \(m\) 同余,记为 \(a \equiv b (mod m)\)。

同余类与剩余系

对于 \(\forall a \in [0,m-1]\),集合 \(\{a+km\}(k\in \Zeta)\) 的所有数 \(mod\) \(m\) 相等,余数均为 \(a\)。

该集合称为一个模 \(m\) 的同余系,简记为 \(\overline{a}\)。

模 \(m\) 的 \(m\) 个同余类构成 \(m\) 的完全剩余系。

\(1 \sim m\) 中有 \(\varphi(m)\) 个数字与 \(m\) 互质,这些数字代表的剩余类构成 \(m\) 的简化剩余系。

简化剩余系关于模 \(m\) 乘法封闭,因为若小于等于 \(m\) 的正整数 \(a,b\) 与 \(m\) 互质,则 \(\gcd(ab,m)=1\)。由余数定义得 \(ab\) \(\text{mod}\) \(m\) 也与 \(m\) 互质,即也属于 \(m\) 的简化剩余系。

费马小定理

若 \(p\) 是质数,则对于任意整数 \(a\),有 \(a^p \equiv a(\bmod p)\)。

OiWiki Proof:

设一个质数为 \(p\),我们取一个不为 \(p\) 倍数的数 \(a\)。

构造一个序列:\(A=\{1,2,3\dots,p-1\}\),这个序列有着这样一个性质:

\[\prod_{i=1}^{p-1}\space A_i\equiv\prod_{i=1}^{p-1} (A_i\times a) \pmod p \]

证明:

\[\because (A_i,p)=1,(A_i\times a,p)=1 \]

又因为每一个 \(A_i\times a \pmod p\) 都是独一无二的,且 \(A_i\times a \pmod p < p\)

得证(每一个 \(A_i\times a\) 都对应了一个 \(A_i\))

设 \(f=(p-1)!\), 则 \(f\equiv a\times A_1\times a\times A_2\times a \times A_3 \dots \times A_{p-1} \pmod p\)

\[\begin{aligned} a^{p-1}\times f &\equiv f \pmod p \\ a^{p-1} &\equiv 1 \pmod p \end{aligned} \]

证毕。

也可用归纳法证明:

显然 \(1^p\equiv 1\pmod p\),假设 \(a^p\equiv a\pmod p\) 成立,那么通过二项式定理有

\[(a+1)^p=a^p+\binom{p}{1}a^{p-1}+\binom{p}{2}a^{p-2}+\cdots +\binom{p}{p-1}a+1 \]

因为 \(\binom{p}{k}=\frac{p(p-1)\cdots (p-k+1)}{k!}\) 对于 \(1\leq k\leq p-1\) 成立,在模 \(p\) 意义下 \(\binom{p}{1}\equiv \binom{p}{2}\equiv \cdots \equiv \binom{p}{p-1}\equiv 0\pmod p\),那么 \((a+1)^p \equiv a^p +1\pmod p\),将 \(a^p\equiv a\pmod p\) 带入得 \((a+1)^p\equiv a+1\pmod p\) 得证。

欧拉定理

若 \(a,n\) 为正整数且互质,则 \(a^{\varphi(n)} \equiv 1(\bmod n)\)。

OiWiki Proof:

实际上这个证明过程跟上文费马小定理的证明过程是非常相似的:构造一个与 \(m\) 互质的数列,再进行操作。

设 \(r_1, r_2, \cdots, r_{\varphi(m)}\) 为模 \(m\) 意义下的一个简化剩余系,则 \(ar_1, ar_2, \cdots, ar_{\varphi(m)}\) 也为模 \(m\) 意义下的一个简化剩余系。所以 \(r_1r_2 \cdots r_{\varphi(m)} \equiv ar_1 \cdot ar_2 \cdots ar_{\varphi(m)} \equiv a^{\varphi(m)}r_1r_2 \cdots r_{\varphi(m)} \pmod{m}\),可约去 \(r_1r_2 \cdots r_{\varphi(m)}\),即得 \(a^{\varphi(m)} \equiv 1 \pmod{m}\)。

当 \(m\) 为素数时,由于 \(\varphi(m) = m - 1\),代入欧拉定理可立即得到费马小定理。

欧拉定理推论

若正整数 \(a,n\) 互质,则对于任意正整数 \(b\),有 \(a^b \equiv a^{b \bmod \varphi(n)}(\bmod n)\)。

当 \(a,n\) 不一定互质时,且 \(b>\varphi(n)\),\(a^b \equiv a^{b \bmod \varphi(n)+\varphi(n)}(\bmod n)\)。

扩展欧拉定理

定义:

\[a^b \equiv \begin{cases} a^{b \bmod \varphi(m)}, &\gcd(a,m) = 1, \\ a^b, &\gcd(a,m)\ne 1, b < \varphi(m), \\ a^{(b \bmod \varphi(m)) + \varphi(m)}, &\gcd(a,m)\ne 1, b \ge \varphi(m). \end{cases} \pmod m \]

OiWiki Proof:

  1. 命题 :\(a\) 的从 \(0\) 次,\(1\) 次到 \(b\) 次幂模 \(m\) 构成的序列中,存在 \(r\) 和 \(s\),使得前 \(r\) 个数(即从 \(a^0 \bmod m\) 到 \(a^{r-1} \bmod m\))互不相同,从第 \(r\) 个数开始,每 \(s\) 个数就循环一次。

    证明

    • 由鸽巢定理易证。

      我们把 \(r\) 称为 \(a\) 幂次模 \(m\) 的循环起始点,\(s\) 称为循环长度。(注意:\(r\) 可以为 \(0\))

      用公式表述为:\(\forall i \ge r, a^i \equiv a^{i+s} \pmod{m}\)

  2. 命题 :\(a\) 为素数的情况,该式成立。

    证明

    • 若模 \(m\) 不能被 \(a\) 整除 ,而因为 \(a\) 是一个素数,那么 \(\gcd(a, m) = 1\) 成立,根据欧拉定理,容易证明该式成立。

    • 若模 \(m\) 能被 \(a\) 整除 ,那么存在 \(r\) 和 \(m'\) 使得 \(m = a^r m'\),且 \(\gcd(a, m')=1\) 成立。所以根据欧拉定理有 \(a^{\varphi(m')} \equiv 1 \pmod{m'}\)。

      又由于 \(\gcd(a^r, m')=1\),所以根据欧拉函数的求值规则,容易得到:\(\varphi(m) = \varphi(m') \times (a-1)a^{r-1}\),即我们有:\(\varphi(m') \mid \varphi(m)\)。

      所以 \(a^{\varphi(m')} \equiv 1 \pmod {m'}, \varphi(m') \mid \varphi(m) \implies a^{\varphi(m)} \equiv 1 \pmod {m'}\),即 \(a^{\varphi(m)}=km'+1\),两边同时乘以 \(a^r\),得 \(a^{r+\varphi(m)} = km + a^r\)(因为 \(m = a^r m'\))

      所以对于 \(m\) 中素因子 \(a\) 的次数 \(r\) 满足:\(a^r \equiv a^{r+\varphi(m)} \pmod m\)。我们可以简单变换形式,得到 推论

      \[b > r \implies a^b \equiv a^{r + ((b-r) \bmod \varphi(m))} \pmod {m} \]

      又由于 \(m = a^r m'\),所以 \(\varphi(m) = \varphi(a^r) \varphi(m') \ge \varphi(a^r)=a^{r-1}(a-1) \ge r\)(tips:\(a\) 是素数,最小是 \(2\),而 \(r \ge 1\))。

      所以因为 \(\varphi(m) \ge r\),故有:

      \[a^r \equiv a^{r+\varphi(m)} \equiv a^{r \bmod \varphi(m)+\varphi(m)} \pmod m \]

      所以

      \[\begin{aligned} a^b &\equiv a^{r+(b-r) \bmod \varphi(m)} \\ &\equiv a^{r \bmod \varphi(m) + \varphi(m) + (b-r) \bmod \varphi(m)} \\ &\equiv a^{\varphi(m) + b \bmod \varphi(m)} \end{aligned} \pmod m \]

      即 \(a^b\equiv a^{b \bmod \varphi(m)+\varphi(m)}\pmod m\)。

  3. 命题 :\(a\) 为素数的幂的情况,该式成立。

    证明

    • 不妨令 \(a = p^k\),是否依然有 \(\forall r, a^{r} \equiv a^{r+\varphi(m)} \pmod m\)?

      答案是肯定的,由命题 1 可知存在 \(s\) 使得 \(a^s\equiv 1 \pmod m\),所以 \(p^{\mathrm{lcm}(s,k)} \equiv 1 \pmod {m}\),所以令 \(s'=\frac{s}{\gcd(s,k)}\) 时,我们能有 \(p^{s'k} \equiv 1 \pmod {m}\)。

      此时有关系:\(s' \mid s\) 且 \(s \mid \varphi(m)\),且 \(r'= \lceil \frac{r}{k}\rceil \le r \le \varphi(m)\),由 \(r',s'\) 与 \(\varphi(m)\) 的关系,依然可以得到 \(a^b\equiv a^{b \bmod \varphi(m)+\varphi(m)}\pmod m\)。

  4. 命题 :\(a\) 为合数的情况,该式成立。

    证明

    • 只证 \(a\) 拆成两个素数的幂的情况,大于两个的用数学归纳法可证。

      设 \(a=a_1a_2\),其中 \(a_i=p_i^{k_i}\),而 \(a_i\) 的循环长度为 \(s_i\);

      则 \(s \mid \operatorname{lcm}(s_1,s_2)\),由于 \(s_1 \mid \varphi(m),s_2 \mid \varphi(m)\),那么 \(\operatorname{lcm}(s_1,s_2) \mid \varphi(m)\),所以 \(s \mid \varphi(m)\),\(r=\max(\lceil \frac{r_i}{k_i} \rceil) \le \max(r_i) \le \varphi(m)\);

      由 \(r,s\) 与 \(\varphi(m)\) 的关系,依然可以得到 \(a^b \equiv a^{b \bmod \varphi(m)+\varphi(m)}\pmod m\)。

      证毕。

以上为形式证明。直观理解如下。

需要知道的是,在 \(\pmod m\) 的条件下,\(a^b \bmod m\) 的取值范围一定在 \([0, m)\),而 \(a^i \bmod m = (a^{i-1} \bmod m) \times a \bmod m\),那么对于任意一个数 \(a\),那么很容易就能知道它的 后继,在有限的空间内这一定会形成一个循环。

在扩展欧拉定理中,循环分为纯循环和混循环。其中纯循环中不存在节点有两个前驱,而混循环则反之。而 \(a^i \mod n\) 形成的序列可以是一个混循环,那么只需要知道循环节的长度,和前面那一小段未进入循环节的长度,就可以根据这个性质来进行降幂了。

值得注意的是,无论是费马小定理,还是(扩展)欧拉定理,一个很重要的应用就是降幂,从而将不可能的表达式化为可能。

扩展欧几里得算法(exgcd)

裴蜀定理

在数论中,裴蜀定理是一个关于最大公约数(或最大公约式)的定理 。

裴蜀定理说明了对任何整数 a、b和它们的最大公约数 d ,关于未知数 x以及 y 的线性的丢番图方程(称为裴蜀等式)。

对于 \(\forall a,b \in \Zeta\),存在一对 \(x,y \in \Zeta\),满足 \(ax+by=\gcd(a,b)\)。

Proof:

欧几里得算法最后一步时,\(b=0\),显然有一对 \(x=1,y=0\),使 \(a*1+0*0=\gcd(a,0)\)。

如果 \(b>0\),则 \(\gcd(a,b)=\gcd(b, a \bmod b)\)。假设存在一对 \(x,y \in \Zeta\),满足 \(b*x+(a \bmod b)*y=\gcd(b, a\bmod b)\),因为 \(bx+(a \bmod b)y=bx+(a-b\lfloor\frac{a}{b}\rfloor)y=ay+b(x-\lfloor\frac{a}{b}\rfloor)y\),所以我们令 \(x`=y,y`=x-\lfloor\frac{a}{b}\rfloor y\),得到 \(ax`+by`=\gcd(a,b)\)。

对于欧几里得算法递归应用数学归纳,可知裴蜀定理成立。

裴蜀定理是按照欧几里得算法思想证明的,且上述证明给出 \(x,y\) 计算方法。这种方法被称为 扩展欧几里得算法

cpp 复制代码
ll exgcd(ll a, ll b, ll &x, ll &y) {
	if (b == 0) {
		x = 1;
		y = 0;
		return a;
	}
	ll res = exgcd(b, a % b, x, y);
	ll z = x;
	x = y;
	y = z - (a / b) * y;
	return res;
}

上述 \(code\) 实现求出方程 \(ax+by=\gcd(a,b)\) 的一组特解,并返回 \(\gcd(a,b)\)。

对于更一般的方程 \(ax+by=c\),有解当且仅当 \(\gcd(a,b)\) 整除 \(c\)。

先求出 \(ax+by=\gcd\) 的一组特解,同时乘 \(\frac{c}{d}\),就得到此方程的一个特解。

实际上,其通解可以表示为:

\[x=\frac{c}{d}x_0+k\frac{b}{d},y=\frac{c}{d}-k\frac{a}{d} \]

其中 \(k \in \Zeta\)。

乘法逆元

\(b,m\in\Zeta\),\(b\) 整除 \(a\),那么存在一个 \(x\in\Zeta\),使 \(\frac{a}{b} \equiv a*x(\bmod m)\)。称 \(x\) 为 \(b\) 的模 \(m\) 乘法逆元 ,记作 \(b^{-1}\)。

乘法逆元可以视情况通过费马小定理或者求解同余方程得到。

ll inv = ksm(p[i] - 1, mod - 2);

或者线性求出 \(1 \sim n\) 所有逆元。

cpp 复制代码
f[0] = 1;	
for(int i=1; i<=n; ++i) //求前缀积,即 i! 
    f[i] = (LL)(f[i-1]*i) % p;
g[n] = quick_pow(f[n], p-2, p); // 求 n!的逆元 
for(int i=n-1; i>=1; --i) // 递推出所有的阶乘的逆元 
    g[i] = (LL)(g[i+1]*(i+1)) % p;
for(int i=1; i<=n; ++i) { // 求出 i 的逆元 
    h[i] = (LL)(g[i]*f[i-1]) % p;
    printf("%lld\n", h[i]);
}

线性同余方程

给定 \(a,b,m\in\Zeta\),求 \(x\in \Zeta\),\(a*x\equiv b(\bmod m)\),或给出无解。

此时未知数指数为 \(1\),所以我们称这种同余方程为一次同余方程,或者为线性同余方程。

原方程可以写成 \(a*x+m*y=b\),那么据裴蜀定理,有解当且仅当 \(\gcd(a,m)\) 整除 \(b\)。

有解时先用 \(exgcd\) 求出 \(a*x_0+b*y_0=\gcd(a,m)\) 的 \(x,y \in \Zeta\)。\(x=x_0*\frac{b}{\gcd(a,m)}\) 为原方程一个解。

通解为所有 \(\bmod \frac{m}{\gcd(a,m)}\) 与 \(x\) 同余的整数。

中国剩余定理(CRT)

一共 \(n\) 个线性同余方程组,求解。

\[\begin{cases} x &\equiv a_1 \pmod {n_1} \\ x &\equiv a_2 \pmod {n_2} \\ &\vdots \\ x &\equiv a_k \pmod {n_k} \\ \end{cases} \]

(其中 \(n_1, n_2, \cdots, n_k\) 两两互质)

  1. 计算所有模数的积 \(n\);

  2. 对于第 \(i\) 个方程:

    1. 计算 \(m_i=\frac{n}{n_i}\);
    2. 计算 \(m_i\) 在模 \(n_i\) 意义下的逆元 \(m_i^{-1}\);
    3. 计算 \(c_i=m_im_i^{-1}\)(不要对 \(n_i\) 取模)。
  3. 方程组在模 \(n\) 意义下的唯一解为:\(x=\sum_{i=1}^k a_ic_i \pmod n\)。

    Proof:

    因为 \(m_i=\frac{n}{n_i}\),是除了 \(n_i\) 以外所有模数的倍数,所以 \(\forall k \not= i,a_i m_i m_i^{-1} \equiv 0(\bmod n_k)\)。又因为 \(a_i m_i m_i^{-1} \equiv a_i(\bmod n_i)\),所以代入 \(x=\sum_{i=1}^k a_i c_i \pmod n\),原方程组成立。

    证毕。

    如果模数不互质,此时我们可以使用 扩展中国剩余定理(exCRT)

    exCRT Solutions。