欧拉函数、欧拉定理、费马小定理、拓展欧拉定理
(1.) 欧拉函数
(1.1) 定义
定义不超过 (m) 且与 (m) 互质的正整数个数为 (phi(m)) ,这个函数称为欧拉函数
例如, (phi(1)=1, phi(3)=2, phi(10)=4)
(1.2) 表达式及其证明
其中 (p_{i}) 是 (m) 的质因子,下面证明这个表达式。
(1)) 若 (m) 为素数,则
(2)) 若 (m) 为素数幂。即 (m=p^{k}) ,其中 (p) 为素数。考虑所有不大于 (m) 且与 (m) 不互质的正整数,他们可以被列举出来,即 (1cdot p, 2cdot p, 3cdot p, ... p^{k -1}cdot p),共计 (p^{k-1}) 个数,所以
(3)) 若 (m) 为合数,那么
由于欧拉函数是积性函数,又 (p_{i}^{k_{i}}) 两两互质,所以
即
即
至此,表达式推导完毕
(1.3) 欧拉函数 (code)
(1.3.1) 求单个数的欧拉函数
根据
计算
int phi(int m)
{
int ans = m;
for(int i = 2; i * i <= m; ++i) {
if(m % i == 0) {
ans -= ans / i;
while(m % i == 0) m /= i;
}
}
if(m > 1) ans -= ans / m;
return ans;
}
(1.3.2) 线性筛一个范围内的欧拉函数
我们知道,在欧拉筛中,合数 (n) 被其最小的质因子 (p) 筛去
设 (n = n'cdot p)
若 (pmid n'),则 (n') 含有 (n) 的所有质因子
所以
若 (p mid n'),则 ((p, n') = 1),由积性函数性质得到
int n, prime[N], phi[N], vis[N];
int Euler_sieve()
{
int cnt = 0;//length of prime table
for(int i = 2; i <= n; ++i)
vis[i] = 1;
for(int i = 2; i <= n; ++i){
if(vis[i]) prime[++cnt] = i, phi[i] = i - 1;//素数的欧拉函数值为 i - 1
for(int j = 1; j <= cnt && prime[j] * i <= n; ++j){
vis[prime[j] * i] = 0;
phi[prime[j] * i] = phi[i] * (prime[j] - 1);
if(i % prime[j] == 0) {
phi[prime[j] * i] = prime[j] * phi[i];
break;
}
}
}
return cnt;
}
(2.) 欧拉定理
若 ((a, m)=1) ,则 (a^{phi(m)}equiv 1 (mod m))
下证欧拉定理
设模 (m) 的一个简化剩余系 (P_{1}),其中的元素为
我们知道
也构成模 (m) 的一个简化剩余系
所以
即
由于 (p_{i}) 与 (m) 互质,所以
所以
所以
即
至此,定理证明完毕
(3.) 费马小定理
若 ((a, m)=1) ,则 (a^{m-1}equiv 1 (mod m))
费马小定理其实是欧拉定理的一种特殊情况,即模数 (m) 是素数时的情况
证明了欧拉定理,也就证明了费马小定理
(4.) 拓展欧拉定理
对于 (a, b, m),若满足
则
先证明三个引理
(1.) 若 (m_{1}, m_{2}, ... , m_{k}) 互质,则由 (xequiv y (mod m_{i}), i = 1, 2, ...\, k),可以推出 (xequiv y (mod m_{1}m_{2}...m_{k}))
(2.) 若 ((a, m)=1),则 (a^{b}equiv a^{b mod phi(m)} (mod m))
(3). 对于素数幂 (p^{k}),满足 (phi(p^{k})geq k, kin mathbb{N})
先证明引理 (1)
若 (m_{1}, m_{2}, ... , m_{k}) 互质
则
则
由
知道 (x - y) 是 (m_{1}, m_{2}, ... , m_{k}) 的公倍数
所以
所以
(Q.E.D)
下证引理 (2)
即
要证明引理 (2),即证明
由于
所以
所以转化为证明
由于
设
结合欧拉定理
由同余性质,将 (q) 个上式相乘即可得到要证明的式子
(Q.E.D.)
下证引理 (3)
要证明
即证明
由于 (p -1geq 1),故考虑证明
由于 (p) 是素数,对 (p) 进行放缩,有 (pgeq 2)
考虑证明 (2^{k - 1}geq k, kin mathbb{N})
构造数列
则
当 (kgeq 1) 时,(f(k + 1) - f(k)geq 0),所以此时数列递增
取最小值,得到
所以
特判 (k = 0) 时
所以
所以
所以
(Q.E.D.)
下证拓展欧拉定理
将 (m) 分解,即
由引理 (1) 知道,若对于每一个 (p_{i})
都有
则待证命题成立
分类讨论 ((a, p_{i}^{k_{i}}))
若
则
所以
由引理 (2) 知道
所以
若
则
设
所以
由于
所以
由引理 (3),结合 (bgeq phi(m)),可以得到不等式
而
所以
所以
综合上述讨论得知,对于每一个 (p_{i}) 都有
所以
(Q.E.D.)
综合一下,欧拉定理即为 (a^{b}equiv a^{b mod phi(m) + phi(m)} (mod m))
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], "
"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int MOD = 1e9 + 7;
const int N = 2e7 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
char c = getchar();
int ans = 0, f = 1;
while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
return ans * f;
}
LL qpow(LL a, LL b, LL m)
{
if(!a) return 0;
LL ans = 1;
while(b) {
if(b & 1) ans *= a, ans %= m;
a *= a, a %= m, b >>= 1;
}
return ans;
}
int phi(int m)
{
int ans = m;
for(int i = 2; i * i <= m; ++i) {
if(m % i == 0) {
ans -= ans / i;
while(m % i == 0) m /= i;
}
}
if(m > 1) ans -= ans / m;
return ans;
}
int main()
{
int a, m;
a = read(), m = read();
int phim = phi(m);
int b = 0, f = 0;
for(char ch = getchar(); isdigit(ch); ch = getchar()) {
b *= 10, b += ch - '0';
if(b >= phim) f = 1, b %= phim;
}
if(f) b += phim; //b < phi(m) 的时候直接快速幂
printf("%d
", qpow(a, b, m));
return 0;
}
(5.) 欧拉定理、费马小定理应用
(5.1) 求逆元
若 ((a, m) = 1),同余式
有唯一解 (xequiv a^{-1} (mod m)),(a^{-1}) 即 (a) 在模 (m) 意义下的逆元
由欧拉定理
所以
特殊的,若 (m) 为素数,(phi(m) = m - 1),所以
根据快速幂,可以在 (O(logm)) 时间求得 (a^{-1})( (m) 是模数)
LL qpow(LL a, LL b, LL m)
{
if(!a) return 0;
LL ans = 1;
while(b) {
if(b & 1) ans *= a, ans %= m;
a *= a, a %= m, b >>= 1;
}
return ans;
}
LL inv(LL a, LL m)
{
return qpow(a, m - 2, m);
}
若 (m) 不是素数,则考虑用拓展欧几里得算法求逆元
int exgcd(int a, int b, int &x, int &y)
{
if(!b) {x = 1, y = 0; return a;}
int r = exgcd(b, a % b, y, x);//y的值被修改为x',x的值被修改为y'
y -= a / b * x;
return r;
}
int inv(int a, int m)
{
int x, y, r;
r = exgcd(a, m, x, y);
while(x < 0) x += m;
return x % m;
}
(5.2) 用欧拉定理化简模数
举个栗子
多组数据,给定 (p),求 (2^{2^{2^{2^{...}}}} mod p)
设 (a = 2^{2^{2^{2^{...}}}})
则问题转化为求
由欧拉定理
继续求解
结合欧拉定理
继续求解
由此可见,这是一个递归求解,然后回溯的过程
(phi) 的值越来越小,最终变为 (1),此时达到递归终点,返回 (0),然后开始回溯,加上 (phi)...
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], "
"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int MOD = 1e9 + 7;
const int N = 1e7 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
char c = getchar();
int ans = 0, f = 1;
while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
return ans * f;
}
LL qpow(LL a, LL b, LL m)
{
if(!a) return 0;
LL ans = 1;
while(b) {
if(b & 1) ans *= a, ans %= m;
a *= a, a %= m, b >>= 1;
}
return ans;
}
int n, prime[N], phi[N], vis[N];
int Euler_sieve(int n = N - 7)
{
phi[1] = 1;
int cnt = 0;//length of prime table
for(int i = 2; i <= n; ++i){
if(!vis[i]) prime[++cnt] = i, phi[i] = i - 1;//素数的欧拉函数值为 i - 1
for(int j = 1; j <= cnt && prime[j] * i <= n; ++j){
vis[prime[j] * i] = 1;
phi[prime[j] * i] = phi[i] * (prime[j] - 1);
if(i % prime[j] == 0) {
phi[prime[j] * i] = phi[i] * prime[j];
break;
}
}
}
return cnt;
}
int t, p;
int solve(int p)
{
if(p == 1) return 0;
return qpow(2, solve(phi[p]) + phi[p], p);
}
int main()
{
t = read();
int l = Euler_sieve();
while(t--) {
p = read();
printf("%d
", solve(p));
}
return 0;
}