比生活简单多了 2017-10-07
欧拉函数,用φ(n)表示
欧拉函数是求小于等于n的数中与n互质的数的数目
比如φ(10),小于等于10的数中与10互质的数有1,3,7,9,所以φ(10)=4
那么,问题来了,如何求求小于等于n的数中与n互质的数的数目???
比如求φ(10)
先质因子分解,10=2*5,再去掉所有2和5的倍数:2的倍数2,4,6,8,10;5的倍数:5,10;
10-10/2-10/5,但是这样算10去掉了两次,那就加回来,10-10/2-10/5+10/2/5=4(容斥原理)
φ(10)=4;
再比如φ(30)
30=2*3*5;
30-30/2-30/3-30/5+ 30/(2*3)+ 30/(2*5)+ 30/(3*5)- 30/(2*3*5)=8
φ(30)=8
但是这样算太麻烦了!
看简单方法:
φ(30) = 30*(1 - 1/2)*(1 - 1/3)*(1 - 1/5) = 30*(1 - 1/2 - 1/3 - 1/5 + 1/6 + 1/10 + 1/15 - 1/30)
可以发现吧,展开后(划线的)跟上面那个式子(斜体的)其实是一样的
于是,可以写代码:
1 #include<cstdio> 2 3 int phi(int x){//欧拉函数 4 int ans = x; 5 for(int i = 2; i*i <= x; i++){ 6 if(x % i == 0){ 7 ans = ans / i * (i-1); 8 while(x % i == 0) x /= i; 9 } 10 } 11 if(x > 1) ans = ans / x * (x-1); 12 return ans; 13 } 14 15 int main() 16 { 17 printf("%d\n",phi(30)); 18 }
时间复杂度为O(√n)
如果要预处理n个数的欧拉函数值,可以用筛子的思想(还记得前面提到的素数筛吗??)
#include<cstdio> const int N = 100000 + 5; int phi[N]; void Euler(){ phi[1] = 1; for(int i = 2; i < N; i ++){ if(!phi[i]){//类似素数筛,phi[i]==0代表i是素数 for(int j = i; j < N; j += i){ if(!phi[j]) phi[j] = j; phi[j] = phi[j] / i * (i-1);//i一定是j的素因子 (看for循环环里面的j的变化) } } } } int main(){ Euler(); printf("%d\n",phi[30]); }
性质:
p为质数
1. phi(p)=p-1 因为质数p除了1以外的因数只有p,故1至p的整数只有p与p不互质
2. 如果i mod p = 0, 那么phi(i * p)=phi(i)*p //例:10%2==0,phi(2*10)=phi(10)*2=4*2=8 正解
3.若i mod p≠0, 那么phi(i * p)=phi(i) * (p-1) //例:10%3!=0,phi(3*10)=phi(10)*(3-1)=4*2=8 正解
公式:
超欧拉取模进化公式