欧拉函数【学习笔记】
一、引文
1.欧拉函数:指不超过n且与n互素的正整数的个数,其中,n是一个正整数。
二、相关知识
1.算数函数:定义在所有正整数上的函数称为算数函数。
2.乘性函数(积性函数):对算数函数ƒ 如果满足对任意两个互素的正整数n和m,均有ƒ(nm)=ƒ(n)ƒ(m)。
3.完全乘性函数(完全积性函数):对任意的两个正整数n和m,均有ƒ(nm)=ƒ(n)ƒ(m)。
三、性质
1.如果p是素数,那么φ(p)=p-1;反之,如果p是一个正整数且满足φ(p)=p-1,那么p是素数。
2.如果p是素数,a是一个正整数,那么φ(pa)=pa-pa-1.
证明:pa不互质的有p×1,p×2,…,p×(pa-1-1),p×pa-1 共pa-1个,那么与它互质的就刚好有pa-pa-1。得证。
3.欧拉函数是积性函数,但不是完全积性函数。
4.推论:当n为奇数时,φ(2n)=φ(n)
证明:由3可知欧拉函数是积性函数,所以φ(2n)=φ(2)φ(n)=φ(n),得证。
5.设n=(p1)a1(p2)a2 … (pk)ak为正整数n的素数幂分解,那么
φ(n)=n•(1-1/p1)•(1-1/p2)•…•(1-1/pk)
有了上面的一些性质,这些证明都比较易证。一般也通过此公式来求解欧拉函数的值。
6.若gcd(n,i) = 1, 那么gcd(n, n-i)=1.
证明:(反证法)先假设gcd(n, n-i)=k且k!=1,那么n%k=0, (n-i)%k=0,可以推出 n = x*k, n-i = y*k,再往下推一步 i = (x-y)*k,这就与gcd(n,i)=1矛盾了。得证。
7.设n是一个大于2的正整数,那么φ(n)是偶数。
8.设n为一个正整数,那么
证明:假设左边=F(n),先证明F(n)是积性函数,直接把F(m)F(n)用上式左边代入后展开,可以看到最后的展开式就是m*n的各个因子的欧拉函数值相加,所以得到F(n)是积性函数。再根据
展开
根据上述性质2
推出
得证。
9.欧拉定理:对任何两个互质的正整数a,m(m≥2),有aφ(m)≡1(mod m).
四、代码实现
1.纯粹且不优雅的无脑实现
兄台,你还能再暴力一点吗!
int phi(int n) { int rea = n; for(int i = 2; i <= n; i++) { if(n%i == 0) //找到了素因子 { rea = rea - rea/i; //rea*(1-1/i) do { n/=i; } while(n%i==0); } } return rea; }
2.线性筛素数表实现
我只想说,我TM之前学的筛素数都是假的!
const int MAXN = 1e5; bool isPrime[MAXN]; int Prime[MAXN], Cnt; void getPrime() { memset(isPrime, 0, sizeof(isPrime)); Cnt = 0; for(int i = 2; i < MAXN; i++) { if(!isPrime[i]) { Prime[Cnt++] = i; } for(int j = 0; j < Cnt && i*Prime[j] < MAXN; j++) { isPrime[i*Prime[j]] = 1; if(i%Prime[j] == 0) break; } } } int phi(int n) { int rea = n; for(int i = 0; Prime[i]*Prime[i] <= n; i++) { if(n%Prime[i] == 0) { rea = rea - rea/Prime[i]; //rea = rea*(1-1/p) do { n/=Prime[i]; }while(n%Prime[i] == 0); } } if(n > 1) rea = rea - rea/n; return rea; }
3.递推求欧拉函数
如果频繁的要用欧拉函数值,就需要先打表。
咦~~,我们好像在哪见过,和埃式筛素数怎么这么像呢~
可以先预先置所有欧拉函数值都为本身。如果再从小到大遍历的过程中,发现该欧拉函数的值与下标相当,就表明该数是素数,然后再通过欧拉函数的求法,乘以(1-1/p)。然后再把有该素因子的数的欧拉函数的值再修改一下,就可以了。复杂度为O(n*ln(n))
const int MAXN = 1e5; int Phi[MAXN]; int Euler() { for(int i = 1; i < MAXN; i++) Phi[i] = i; for(int i = 2; i < MAXN; i+=2) Phi[i]/=2; for(int i = 3; i < MAXN; i+=2) { if(Phi[i] == i) //i是素数 { for(int j = i; j < MAXN; j+=i) { Phi[j] = Phi[j]/i*(i-1); // x*(1-1/i) Phi[j]有素因子i } } } }
4.线性欧拉函数值
void Pre() { tot = 0; memset(isPrime, 1, sizeof(isPrime)); isPrime[0] = isPrime[1] = 0; Phi[1] = 1; for(int i = 2; i < maxn; i++) { if(isPrime[i]) { Prime[tot++] = i; Phi[i] = i - 1; } for(int j = 0; (ll)i * Prime[j] < maxn && j < tot; j++) { isPrime[i * Prime[j]] = 0; if(i % Prime[j] == 0) { Phi[i * Prime[j]] = Prime[j] * Phi[i]; break; } Phi[i * Prime[j]] = Phi[i] * (Prime[j] - 1); } } }
五、扩展学习
下面是学习的另外一篇博主的博客,他写的比我好,我把他没证的一点内容证了,在此感谢这位博主。
https://www.cnblogs.com/linyujun/p/5194170.html
另一种,比上面更快的方法
需要用到如下性质
p为质数
1. phi(p)=p-1 因为质数p除了1以外的因数只有p,故1至p的整数只有p与p不互质
2. 如果i mod p = 0, 那么 phi(i * p)=phi(i) * p (我不会证明)
3.若i mod p ≠0, 那么 phi( i * p )=phi(i) * ( p-1 ) (我不会证明)
(所以我说我会证明都是骗人的╮( ̄▽ ̄)╭)
第一个证明如下,第二个证明原理相同,只不过i没有素因子p。
欧拉函数是积性函数,能分开的前提是gcd(a,b)=1
素数次幂的欧拉函数值反向运用
得证。
const int MAXN = 1e6; int Prime[MAXN], Phi[MAXN]; int tot; //记录素数个数 void Euler() { memset(Phi, 0, sizeof(Phi)); Phi[1] = 1; tot = 0; for(int i = 2; i < MAXN; i++) { if(!Phi[i]) //i为素数 { Prime[tot++] = i; Phi[i] = i - 1; } for(int j = 0; j < tot && (long long)Prime[j]*i < MAXN; j++) { if(i%Prime[j]) { Phi[ i*Prime[j] ] = Phi[i]*(Prime[j] - 1); } else // Prime[j]是i的素因子 { Phi[ i*Prime[j] ] = Phi[i]*Prime[j]; break; } } } }
还有以下几个实用的公式
假设a与p互质
因为
所以
如果p是素数
这个公式的意义在于在求指数幂的时候,如果b非常大,我们可以不用死求a^b的快速幂,可以先对b取模再求。