欧拉函数
Posted de-compass
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了欧拉函数相关的知识,希望对你有一定的参考价值。
定义
(varphi(n)=ncdot(1-frac 1{p_1})cdot(1-frac 1{p_2})cdot...cdot(1-frac 1{p_n}))
其中(p_i)表示(i)的质因数
特别地,(varphi(1)=1)
意义:对于正整数(n),(varphi(n))表示小于等于(n)的正整数中与(n)互素的数的数目
证明
用类似于容斥原理的方法求(1sim n-1)中与(n)互素的数:
先把(n)的所有质因数(p_i)的倍数都筛掉,再把(p_i,p_{i+1})的公共倍数添加回来,再去掉(p_i,p_{i+1},p_{i+2})的公共倍数...
得到(varphi(n)=n-frac n{p_1}-frac n{p_2}-cdots+frac n{p_1p_2}+frac n{p_2p_3}+cdots)
由相关数学知识化简得到定义式。
求解方法
由定义式可以得到一种直白的求法:(在线算法)
用唯一分解定理的方法找出(n)的所有质因数,同时用定义式求即可
由于(p_i)是(n) 不同的质因数,所以计算中除法不会出现不能整除的情况
为了防止爆精度,计算时先除后乘。
int phi(int n) { if(n == 1) return 1; int p = n; for(int i=2, n1=n; i*i<=n1; i++) if(n%i == 0){ p = p / i * (i-1); while(n%i == 0) n /= i; } if(n>1) p = p / n * (n-1); return p; }
打欧拉函数表:(离线算法)用筛法,边筛素数边算
埃氏筛
初始化(phi[i]=i)
筛到素数(p),在标记(p)的倍数不是素数的同时计算,(phi[i*p]=phi[i*p]/p*(p-1))
void sieve(int n) { phi[1] = 1; for(int i=2; i<=n; i++) { if(!npr[i]) continue; for(int j=2; i*j<=n; j++) { npr[i*j] = 1; phi[i*j] = phi[i*j] / i * (i-1); } } }
欧拉筛(线性筛)
因为欧拉筛中每个数只筛一次,所以要一次算出最终结果。
设(p)为素数,分类讨论如下:
- (phi[p]=p-1)
- 已知(phi[x])且(p)能整除(x):(phi[x*p]=phi[x]*p)
- 已知(phi[x])且(p)不能整除(x):(phi[x*p]=phi[x]*(p-1))
简单证明:
- (p)以内所有数都与(p)互质,所以答案为(p-1)
- p是x的质因数,所以从定义式看从(phi[x])到(phi[x*p])后面带括号部分是相同的,只是前面的(x)变成了(x*p)
- p不是x的质因数,所以(p)是(x*p)新加入的质因数,所以前面的(x)变成(x*p)的同时后面要乘上因子(frac {p-1}p),消去即相当于乘(p-1)
void sieve(int n) { np = 0; phi[1] = 1; for(int i=2; i<=n; i++) { if(!npr[i]) p[++np] = i, phi[i] = i-1; for(int j=1; j<=np && i*p[j]<=n; j++) { npr[i*p[j]] = 1; phi[i*p[j]] = phi[i] * (i%p[j] ? p[j]-1 : p[j]); if(i%p[j] == 0) break; } } }
以上是关于欧拉函数的主要内容,如果未能解决你的问题,请参考以下文章