数论基础
【第四讲】
第四讲我们要学习的概念有:
- 欧拉函数
- 欧拉定理
- 扩展欧拉定理
一、欧拉函数
在上一讲中,我们了解到了欧拉函数计算逆元的方法,接下来我们来详细探究欧拉函数
1.1 定义
欧拉函数 表示小于等于
的正整数中与
互质(
)的数的个数
例如:
-
:小于等于 6 且与 6 互质的数有 1 和 5,因此
-
:小于等于 7 且与 7 互质的数有 1、2、3、4、5、6,因此
-
:小于等于 8 且与 8 互质的数有 1、3、5、7,因此
1.2 计算公式
让我们回忆一下算术基本定理,每个大于1的正整数都可以唯一地表示为有限个质数的乘积
其中 表示质数,则 欧拉函数
特别地,当 为质数时,
,即
和
的数均互质
另外,欧拉函数为 积性函数 ,满足
欧拉函数计算公式的推导可以从 概率论 和 容斥原理 两个角度展开,以下给出详细证明过程:
1.2.1 从概率论角度推导
核心思想
欧拉函数 表示小于等于
的数中与
互质的数的比例,即
。我们可以通过计算每个质因数不整除随机数的概率,然后利用独立性假设得到这个比例。
推导步骤
-
质因数分解:设
,其中
是
的不同质因数。
-
事件定义:对于每个质因数
,定义事件
为“一个随机数
能被
整除”。
-
概率计算:
- 事件
发生的概率为
(因为在
到
中,每
个数中有一个能被
整除)。
- 事件
不发生的概率为
。
- 事件
-
独立性假设:假设事件
相互独立(实际上在模
下近似成立),则所有事件都不发生的概率为:
-
结论:与
互质的数的比例即为上述概率,因此:
1.2.2 从集合容斥原理角度推导
核心思想
利用容斥原理计算 到
中不被任何质因数
整除的数的个数。
推导步骤
-
全集与子集:设全集
,子集
(即被
整除的数)。
-
目标:计算
,即不被任何
整除的数的个数。
-
容斥原理公式:
-
计算各交集的大小:
(被
整除的数的个数)。
(同时被
和
整除的数的个数)。
- 一般地,
。
-
代入容斥公式:
-
化简:
注意到右边括号内的表达式恰好是乘积展开式:
因此
1.2.3 示例
以 为例验证两种推导方法:
- 质因数分解:
,质因数为
和
- 概率论角度:
- 随机数不被
整除的概率:
- 随机数不被
整除的概率:
- 两者同时成立的概率:
- 因此
,与实际结果一致(
和
与
互质)
- 随机数不被
- 容斥原理角度:
- 被
整除的数:
(共
个)
- 被
整除的数:
(共
个)
- 同时被
和
整除的数:
(共
个)
- 根据容斥公式:
,因此不被任何质因数整除的数有
个
- 被
1.3 代码
1.3.1 计算欧拉函数
C++:
#include <bits/stdc++.h>
using namespace std;
// 计算单个n的欧拉函数φ(n)
long long euler_phi(long long n) {
long long res = n; // 初始值为n
// 分解质因数
for (long long p = 2; p * p <= n; ++p) {
if (n % p == 0) { // p是n的质因数
// 乘以(1 - 1/p),即先除以p再乘以(p-1)
res = res / p * (p - 1);
// 去除所有p的因子(避免重复计算)
while (n % p == 0) {
n /= p;
}
}
}
// 若剩余n是大于1的质数(未被分解)
if (n > 1) {
res = res / n * (n - 1);
}
return res;
}
// 测试
int main() {
cout << euler_phi(6) << endl; // 输出:2(1,5与6互质)
cout << euler_phi(7) << endl; // 输出:6(1-6与7互质)
cout << euler_phi(8) << endl; // 输出:4(1,3,5,7与8互质)
return 0;
}
Python:
def euler_phi(n):
res = n # 初始值为n
# 分解质因数
p = 2
while p * p <= n:
if n % p == 0: # p是n的质因数
# 乘以(1 - 1/p)
res = res // p * (p - 1)
# 去除所有p的因子
while n % p == 0:
n //= p
p += 1
# 剩余n为质数
if n > 1:
res = res // n * (n - 1)
return res
# 测试
print(euler_phi(6)) # 输出:2
print(euler_phi(7)) # 输出:6
print(euler_phi(8)) # 输出:4
1.3.2 预处理欧拉函数
C++:
#include <bits/stdc++.h>
using namespace std;
// 预处理1到n的欧拉函数,返回phi数组(phi[0]无用,phi[1..n]有效)
vector<int> euler_phi_sieve(int n) {
vector<int> phi(n + 1);
// 初始化:phi[i] = i
for (int i = 0; i <= n; ++i) {
phi[i] = i;
}
// 筛法更新
for (int p = 2; p <= n; ++p) {
if (phi[p] == p) { // p是质数(未被处理过)
for (int m = p; m <= n; m += p) { // 遍历p的所有倍数
phi[m] = phi[m] / p * (p - 1);
}
}
}
return phi;
}
// 测试
int main() {
int n = 10;
vector<int> phi = euler_phi_sieve(n);
for (int i = 1; i <= n; ++i) {
cout << "phi(" << i << ") = " << phi[i] << endl;
}
return 0;
}
Python:
def euler_phi_sieve(n):
phi = list(range(n + 1)) # 初始化:phi[i] = i
for p in range(2, n + 1):
if phi[p] == p: # p是质数
for m in range(p, n + 1, p): # 遍历p的倍数
phi[m] = phi[m] // p * (p - 1)
return phi
# 测试
n = 10
phi = euler_phi_sieve(n)
for i in range(1, n + 1):
print(f"phi({i}) = {phi[i]}")
二、欧拉定理
2.1 定理内容
若整数 与正整数
互质,即
,则
其中 是欧拉函数,表示小于等于
且与
互质的正整数的个数
证明:
-
构造简化剩余系:设模
的简化剩余系为
,其中每个
满足
且
。
-
生成新的简化剩余系:由于
与
互质,将
中每个元素乘以
后,得到集合
性质1 :每个
仍与
互质(因为
且
,乘积的最大公约数为1)
性质2:
中元素模
两两不同余(若
,则由
可消去
,得
与
是简化剩余系矛盾
因此,
也是模
的简化剩余系,与
包含的元素完全相同(仅顺序可能不同)
-
乘积相等性:由于
和
是同一集合的不同排列,它们的元素乘积模
相等:
-
化简推导:
右边乘积可整理为
因此有
由于每个
与
互质,它们的乘积也与
互质(即
),因此可两边同时消去乘积项,得:
即
,定理得证
2.2 费马小定理
欧拉定理 是 费马小定理 的推广,而 费马小定理 是 欧拉定理 的特殊情况
当 为质数
时,欧拉函数
,此时欧拉定理退化为费马小定理
2.3 欧拉降幂公式
欧拉降幂的本质是利用欧拉定理将指数 压缩为一个较小的数,使得
的结构不变
由欧拉定理,若 ,则
因此,对于任意指数 ,可将其表示为
其中 是整数,
此时
即
示例:
计算。
步骤:
计算:7是质数,
;
验证,满足基本降幂条件
降幂:指数,因
,故
计算:
结果:。
C++:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// 计算最大公约数
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
// 计算欧拉函数φ(n)
ll euler_phi(ll n) {
ll res = n;
for (ll p = 2; p * p <= n; p++) {
if (n % p == 0) {
while (n % p == 0) n /= p;
res -= res / p;
}
}
if (n > 1) res -= res / n;
return res;
}
// 快速幂:计算 (a^b) mod m
ll fast_pow(ll a, ll b, ll m) {
ll res = 1;
a %= m;
while (b > 0) {
if (b & 1) res = (res * a) % m;
a = (a * a) % m;
b >>= 1;
}
return res;
}
// 判断大指数字符串b是否 >= phi(m)
bool is_b_ge_phi(const string &b_str, ll phi) {
// 若phi为0,特殊处理(实际phi(m)>=1)
if (phi == 0) return true;
// 将phi转为字符串比较
string phi_str = to_string(phi);
if (b_str.size() > phi_str.size()) return true;
if (b_str.size() < phi_str.size()) return false;
// 长度相等时逐位比较
return b_str >= phi_str;
}
// 大指数字符串b对mod取模
ll mod_big(const string &b_str, ll mod) {
ll res = 0;
for (char c : b_str) {
res = (res * 10 + (c - '0')) % mod;
}
return res;
}
// 欧拉降幂主函数(支持大指数字符串输入)
ll euler_pow(ll a, const string &b_str, ll m) {
if (m == 1) return 0; // 任何数模1都为0
ll phi = euler_phi(m);
ll g = gcd(a, m);
// 计算降幂后的指数
ll exp;
if (g == 1) {
// 基本降幂:a与m互质
exp = mod_big(b_str, phi);
} else {
// 扩展降幂:a与m不互质
if (is_b_ge_phi(b_str, phi)) {
exp = mod_big(b_str, phi) + phi;
} else {
// 指数小于phi,直接转换为整数计算
exp = stoll(b_str); // 注意:若b_str过长可能溢出,实际可优化为逐位计算幂
}
}
// 计算降幂后的结果
return fast_pow(a, exp, m);
}
int main() {
// 示例:计算5^1000000000 mod 7
ll a = 5;
string b_str = "1000000000"; // 大指数用字符串表示
ll m = 7;
cout << euler_pow(a, b_str, m) << endl; // 输出:2
return 0;
}
Python:
import math
# 计算欧拉函数φ(n)
def euler_phi(n):
res = n
p = 2
while p * p <= n:
if n % p == 0:
while n % p == 0:
n //= p
res -= res // p
p += 1
if n > 1:
res -= res // n
return res
# 快速幂:计算 (a^b) mod m
def fast_pow(a, b, m):
res = 1
a %= m
while b > 0:
if b & 1:
res = (res * a) % m
a = (a * a) % m
b >>= 1
return res
# 欧拉降幂主函数(支持大指数,Python天然支持大整数)
def euler_pow(a, b, m):
if m == 1:
return 0 # 任何数模1都为0
phi = euler_phi(m)
g = math.gcd(a, m)
# 计算降幂后的指数
if g == 1:
# 基本降幂:a与m互质
exp = b % phi
else:
# 扩展降幂:a与m不互质
if b >= phi:
exp = b % phi + phi
else:
exp = b # 指数小于phi,直接使用原指数
# 计算降幂后的结果
return fast_pow(a, exp, m)
# 示例:计算5^1000000000 mod 7
a = 5
b = 1000000000 # Python支持直接处理大整数
m = 7
print(euler_pow(a, b, m)) # 输出:2
三、扩展欧拉定理
欧拉定理要求 与
互质,即
,但实际问题中并不能总是满足这一情况,于是就有了对欧拉定理的扩展, 扩展欧拉定理
3.1 定理内容
若 是正整数,
是任意整数,
是正整数,则:
特别地,当 时,无论
是否大于
,均可以使用
即欧拉降幂公式
3.2 定理证明
扩展欧拉定理的证明需分情况讨论,核心思路是利用质因数分解和数学归纳法
情况1 :
此时退化为欧拉定理,直接应用欧拉降幂公式,即
情况2 : 且
需证
(1)质因数分解:设 ,只需证明对每个质数幂
成立,即
再由 中国剩余定理(CRT) 合并
(2)单质数幂的证明:
-
设
是质数,
,需证
其中
-
若
(即
含质因数
),设
(
且
):
-
当
时,
且
等式成立
-
当
时,由欧拉定理
故
两边同乘
得
-
(3)合并结果:对所有质数幂成立,故对 成立。
3.3 示例
计算 :
步骤1:分解 ,计算
。
步骤2:,且
,适用扩展形式。
步骤3:降幂:。
步骤4:计算 ,故
。
C++:
#include <bits/stdc++.h>
using namespace std;
// 计算欧拉函数φ(n)
int euler_phi(int n) {
int res = n;
for (int p = 2; p * p <= n; ++p) {
if (n % p == 0) {
while (n % p == 0) n /= p;
res -= res / p;
}
}
if (n > 1) res -= res / n;
return res;
}
// 快速幂:计算 (base^exp) % mod
int fast_pow(int base, int exp, int mod) {
int result = 1;
base %= mod; // 确保base在模范围内
while (exp > 0) {
if (exp % 2 == 1) result = (result * base) % mod;
base = (base * base) % mod;
exp /= 2;
}
return result;
}
// 扩展欧拉定理计算:a^b mod m(处理a与m不互质的情况)
int extended_euler(int a, int b, int m) {
if (m == 1) return 0; // 任何数模1为0
int phi = euler_phi(m);
int g = gcd(a, m);
// 扩展欧拉定理条件:a与m不互质且b >= phi(m)
if (g != 1 && b >= phi) {
int exp = (b % phi) + phi; // 降幂公式
return fast_pow(a, exp, m);
} else {
// 不满足扩展条件时直接计算(包括b < phi的情况)
return fast_pow(a, b, m);
}
}
int main() {
// 示例:计算2^1000 mod 12
int a = 2, b = 1000, m = 12;
cout << extended_euler(a, b, m) << endl; // 输出:4
return 0;
}
Python:
import math
# 计算欧拉函数φ(n)
def euler_phi(n):
res = n
p = 2
while p * p <= n:
if n % p == 0:
while n % p == 0:
n //= p
res -= res // p
p += 1
if n > 1:
res -= res // n
return res
# 快速幂:计算 (base^exp) % mod
def fast_pow(base, exp, mod):
result = 1
base %= mod # 确保base在模范围内
while exp > 0:
if exp % 2 == 1:
result = (result * base) % mod
base = (base * base) % mod
exp //= 2
return result
# 扩展欧拉定理计算:a^b mod m(处理a与m不互质的情况)
def extended_euler(a, b, m):
if m == 1:
return 0 # 任何数模1为0
phi = euler_phi(m)
g = math.gcd(a, m)
# 扩展欧拉定理条件:a与m不互质且b >= phi(m)
if g != 1 and b >= phi:
exp = (b % phi) + phi # 降幂公式
return fast_pow(a, exp, m)
else:
# 不满足扩展条件时直接计算(包括b < phi的情况)
return fast_pow(a, b, m)
# 示例:计算2^1000 mod 12
a = 2
b = 1000
m = 12
print(extended_euler(a, b, m)) # 输出:4