欧拉定理
欧拉定理(Euler's Totient Theorem)
一、核心思想:指数的周期性
在模 \(n\) 运算上,不断地用一个数 \(a\) 去乘以自己时(即 \(a, a^2, a^3, \dots\)),会发现结果会呈现出周期性循环。
欧拉定理对这个循环的长度给出了一个明确的结论:
只要 \(a\) 和 \(n\) 互质,那么 \(a\) 的 \(\phi(n)\) 次方模 \(n\) 的结果,必然是 \(1\)。
这里的 \(\phi(n)\) 就是欧拉函数。
二、定理的正式表述
如果整数 \(a\) 和 正整数 \(n\) 满足 \(\gcd(a,n)=1\)(即它们互质),那么:\(a^{\phi(n)} \equiv 1 \pmod{n}\)。
这个定理是费马小定理的推广。当 \(n\) 是一个质数 \(p\) 时,\(\phi(p) = p-1\),定理就变成了 \(a^{p-1} \equiv 1 \pmod{p}\),这正是费马小定理。
三、应用:降幂
这个定理的厉害之处在于,能把很大的指数给降下来。既然 \(a^{\phi(n)}\) 等于 \(1\),那么 \(a\) 的 \(k \cdot \phi(n)\) 次方也等于 \(1\)。这意味着指数 \(b\) 可以对 \(\phi(n)\) 取模。
所以,计算 \(a^b \bmod n\) 时,可以把它简化为:\(a^b \equiv a^{b \bmod \phi(n)} \pmod{n}\)。
例子:计算 \(3^{100} \bmod 7\)。
- 检查条件:\(\gcd(3,7)=1\),互质。满足条件。
- 计算 \(\phi\) 值:\(7\) 是质数,\(\phi(7)=7-1=6\)。
- 降幂:指数 \(100\) 可以对 \(6\) 取模。\(100 \bmod 6 = 4\)。
- 简化问题:原问题等价于计算 \(3^4 \bmod 7\)。
- 计算:\(3^4 = 81\)。\(81 \div 7 = 11 \cdots 4\)。
- 答案:\(3^{100} \bmod 7 = 4\)。
例题:P10496 [ICPC-Hefei 2008 Online] The Luckiest Number
给定一个正整数 \(L \ (L \le 2 \times 10^9)\),问至少多少个 8 连在一起组成的正整数是 \(L\) 的倍数?
设由 \(n\) 个 8 组成的数字为 \(x\),\(x = 8 + 80 + 800 + \cdots + 8 \times 10^{n-1} = 8 \times \dfrac{10^n-1}{9}\)。题目要求 \(x \equiv 0 \pmod{L}\),即 \(8(10^n-1) \equiv 0 \pmod{9L}\)。
为了简化同余式,设 \(g = \gcd(8,9L)\),因为 \(\gcd(8,9)=1\),所以 \(g=\gcd(8,L)\)。将同余式两端及模数同时除以 \(g\),得到 \(\dfrac{8}{g} (10^n-1) \equiv 0 \pmod{\dfrac{9L}{g}}\)。
因为 \(\gcd(\dfrac{8}{g}, \dfrac{9L}{g}) = 1\),根据同余式的性质,可以消去 \(\dfrac{8}{g}\),得到 \(10^n-1 \equiv 0 \pmod{\dfrac{9L}{g}}\),即 \(10^n \equiv 1 \pmod{\dfrac{9L}{g}}\)。
令 \(M = \dfrac{9L}{\gcd(8,L)}\),问题转化为求满足 \(10^n \equiv 1 \pmod{M}\) 的最小正整数 \(n\)。
根据欧拉定理,\(10^n \equiv 1 \pmod{M}\) 有解当且仅当 \(\gcd(10,M)=1\)。若不满足,则输出 0。
满足条件的最小正整数 \(n\) 一定是 \(\phi(M)\) 的约数。
证明
反证法,假设满足 \(10^n \equiv 1 \pmod{M}\) 的最小正整数 \(n_0\) 不能整除 \(\phi(M)\)。
设 \(\phi(M) = q n_0 + r \ (0 \lt r \lt n_0)\),因为 \(10^{n_0} \equiv 1 \pmod{M}\),所以 \(10^{q n_0} \equiv 1 \pmod{M}\)。根据欧拉定理,有 \(10^{\phi(M)} \equiv 1 \pmod{M}\),所以 \(10^r \equiv 1 \pmod{M}\)。这与 \(n_0\) 最小矛盾,故假设不成立。
根据这个分析,只需求出欧拉函数 \(\phi(M)\),枚举它的所有约数,用快速幂逐一检查是否满足条件即可,时间复杂度为 \(O(\sqrt{L} \log L)\)。
参考代码
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
using ll = long long;
// 计算最大公约数
ll gcd(ll x, ll y) {
return y == 0 ? x : gcd(y, x % y);
}
// 计算欧拉函数 phi(n)
ll calc(ll n) {
ll res = n;
for (ll i = 2; i * i <= n; i++) {
if (n % i == 0) {
res = res / i * (i - 1);
while (n % i == 0) n /= i;
}
}
if (n > 1) res = res / n * (n - 1);
return res;
}
// 防止计算 a * b % m 时溢出
ll mul(ll a, ll b, ll m) {
return (__int128)(a) * b % m;
}
// 快速幂计算 a^b % m
ll qpow(ll a, ll b, ll m) {
ll res = 1; a %= m;
while (b > 0) {
if (b & 1) res = mul(res, a, m);
a = mul(a, a, m);
b >>= 1;
}
return res;
}
int main()
{
int cnt = 0;
while (true) {
ll l; scanf("%lld", &l);
if (l == 0) break;
cnt++;
printf("Case %d: ", cnt);
// m = 9l / gcd(8, 9l),由于 gcd(8, 9) = 1,可简化为 9l / gcd(8, l)
ll m = l * 9 / gcd(8, l);
// 若 gcd(10, m) != 1,则 10^n = 1 (mod m) 无解
if (gcd(10, m) != 1) {
printf("0\n");
continue;
}
ll phi = calc(m);
vector<ll> divs;
// 寻找 phi(m) 的所有约数
for (ll i = 1; i * i <= phi; i++) {
if (phi % i == 0) {
divs.push_back(i);
if (i * i != phi) divs.push_back(phi / i);
}
}
// 按从小到大排序约数
sort(divs.begin(), divs.end());
ll ans = 0;
// 第一个满足 10^d = 1 (mod m) 的约数即为最小解
for (ll d : divs) {
if (qpow(10, d, m) == 1) {
ans = d;
break;
}
}
printf("%lld\n", ans);
}
return 0;
}
扩展欧拉定理(Extended Euler's Theorem)
一、为什么需要“扩展”?
欧拉定理有一个严格的限制:\(a\) 和 \(n\) 必须互质。那如果不互质怎么办?比如计算 \(6^{100} \bmod 10\),\(\gcd(6,10)=2\),欧拉定理就没法直接用了。
扩展欧拉定理就是来解决这个问题的。它是一个更通用的版本,对 \(a\) 和 \(n\) 没有任何限制。
二、定理的正式表述
对于任意整数 \(a\) 和正整数 \(n\),分情况讨论指数 \(b\):
- 当 \(b \lt \phi(n)\) 时:
- 指数 \(b\) 还不够大,可能还没进入“循环节”。
- 这时候不能降幂,老老实实直接计算。
- 当 \(b \ge \phi(n)\) 时:
- 指数 \(b\) 足够大,保证它已经进入了数字的循环节。
- 这时候就可以安全地降幂了,但公式稍微有点不同。
- \(a^b \equiv a^{b \bmod \phi(n) + \phi(n)} \pmod{n}\)。
注意这个 \(+ \phi(n)\)。这是扩展欧拉定理的精髓,也是和标准欧拉定理唯一的区别。
为什么要 \(+ \phi(n)\)?这是一个“安全措施”。因为当 \(a\) 和 \(n\) 不互质时,幂的循环可能不会从 \(a^0=1\) 开始。前面可能有一小段“尾巴”不参与循环。\(b \bmod \phi(n)\) 可能会得到一个很小的数,这可能会跳回到“尾巴”上。而 \(b \bmod \phi(n) + \phi(n)\) 保证了新的指数一定大于等于 \(\phi(n)\),安全地留在循环节内。
例子:计算 \(6^{100} \bmod 10\)
- 检查条件:\(\gcd(6,10)=2\),不互质。必须用扩展欧拉定理。
- 计算 \(\phi\) 值:\(\phi(10)= 10 \times (1-1/2) \times (1-1/5) = 4\)。
- 比较指数:指数 \(b=100\),\(\phi(10)=4\)。因为 \(100 \ge 4\),适用第二种情况。
- 降幂:\(100 \bmod 4 = 0\)。新的指数是 \(0 + \phi(10) = 4\)。
- 简化问题:原问题等价于计算 \(6^4 \bmod 10\)。
- 计算:\(6^1=6, 6^2=36 \equiv 6, 6^3 \equiv \dots 6\)。\(6\) 的任意正整数次幂模 \(10\) 都是 \(6\)。所以 \(6^4 \bmod 10 = 6\)。
- 答案:\(6^{100} \bmod 10 = 6\)。
习题:P5091 【模板】扩展欧拉定理
解题思路
先求 \(\phi(m)\),然后求 \(b \bmod \phi(m)\),在拼数的过程中取余来求。
中间判断一下拼数的过程中是否出现了 \(\ge \phi(m)\) 的情况。如果没出现,就是 \(b \lt \phi(m)\) 的情况,也就是要求 \(a^b\),否则最后是求 \(a^{b \bmod \phi(m) + \phi(m)}\)。
参考代码
#include <cstdio>
const int N = 20000005;
char s[N];
int calcPhi(int x) {
int res = x;
int i = 2;
while (i * i <= x) {
if (x % i == 0) {
res = res / i * (i - 1);
while (x % i == 0) x /= i;
}
i++;
}
if (x > 1) res = res / x * (x - 1);
return res;
}
int qpow(int x, int y, int mod) {
int res = 1;
while (y > 0) {
if (y & 1) res = 1ll * res * x % mod;
x = 1ll * x * x % mod;
y >>= 1;
}
return res;
}
int main()
{
int a, m; scanf("%d%d%s", &a, &m, s);
int phi = calcPhi(m);
int b = 0;
bool big = false;
for (int i = 0; s[i]; i++) {
b = b * 10 + s[i] - '0';
if (b >= phi) {
b %= phi; big = true;
}
}
if (big) b += phi;
printf("%d\n", qpow(a, b, m));
return 0;
}

浙公网安备 33010602011771号