欧拉函数算法实现_编程代码

(52) 2024-08-14 23:01:01

1.欧拉函数

φ(n):1~n内,与n互质的数的个数
φ(6)=2,1 2 3 4 5 6

任意正整数n: n = p 1 a 1 p 2 a 2 . . . p m a m n=p^{a_1}_1p^{a_2}_2...p^{a_m}_m n=p1a1p2a2...pmam ( p i p_i pi 为素数)
欧拉函数计算公式: φ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) . . . ( 1 − 1 p m ) φ(n)=n(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_m}) φ(n)=n(1p11)(1p21)...(1pm1)
证明:(容斥原理)
1.从1~n中去掉 p 1 、 p 2 、 . . . 、 p m p_1、p_2、...、p_m p1p2...pm的所有倍数(剩余个数 N 1 = n − n p 1 − n p 2 − . . . − n p m N_1=n-\frac{n}{p_1}-\frac{n}{p_2}-...-\frac{n}{p_m} N1=np1np2n...pmn
2.加上所有 p i ∗ p j p_i*p_j pipj的倍数( N 2 = N 1 + n p 1 ∗ p 2 + n p 1 ∗ p 3 + . . . + n p 2 ∗ p 3 + . . . n p n − 1 ∗ p n N_2=N_1+\frac{n}{p_1*p_2}+\frac{n}{p_1*p3}+...+\frac{n}{p_2*p_3}+...\frac{n}{p_{n-1}*p_n} N2=N1+p1p2n+p1p3n+...+p2p3n+...pn1pnn
3.减去所有 p i ∗ p j ∗ p k p_i*p_j*p_k pipjpk的倍数( N 3 = N 2 − n p 1 ∗ p 2 ∗ p 3 − . . . N_3=N_2-\frac{n}{p_1*p_2*p_3-...} N3=N2p1p2p3...n

进行算术处理后,就会知道 ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) . . . ( 1 − 1 p m ) (1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_m}) (1p11)(1p21)...(1pm1)展开后就会是上述容斥原理所要求的式子。

时间复杂度分析:

通过上式直接进行计算,算法的时间复杂度瓶颈在于分解质因数O(√n)。

例题

欧拉函数算法实现_编程代码 (https://mushiming.com/)  第1张

输入
3 3 6 8 
输出
2 2 4 
代码

O ( n a i ) O(n\sqrt a_i) O(na
i
)
——> 4 ∗ 1 0 6 4*10^6 4106~ 5 ∗ 1 0 6 5*10^6 5106

#include <iostream> #include <algorithm> #include <unordered_map> #include <vector> const int maxn=2010; const int mod=1e9+7; using namespace std; int phi(int x) { 
    int ans=x; for(int i=2;i<=x/i;i++) if(x%i==0) { 
    ans=ans/i*(i-1); while(x%i==0) x/=i; } if(x>1) ans=ans/x*(x-1); return ans; } int main() { 
    int n; cin>>n; while(n--) { 
    int x; cin>>x; cout<<phi(x)<<endl; } return 0; } 
2.筛法求欧拉函数 O(n)

如果我们要求出1~n中每一个数的欧拉函数,如果我们还是用公式去算,那么我们一个个去分解质因数就很慢,整个程序时间复杂度就是O(n√n),所以要用筛法。

例题

欧拉函数算法实现_编程代码 (https://mushiming.com/)  第2张

primse[j]是否在i中出现过 只跟质因子有关,跟质因子出现的次数无关

i%primes[j]==0
φ ( i ) = i ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) . . . ( 1 − 1 p k ) . . . ( 1 − 1 p k ) φ(i)=i(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_k})...(1-\frac{1}{p_k}) φ(i)=i(1p11)(1p21)...(1pk1)...(1pk1) pj出现过
φ ( p j ∗ i ) = p j ∗ i ∗ ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) . . . ( 1 − 1 p k ) . . . ( 1 − 1 p k ) = p j ∗ φ ( i ) φ(p_j*i)=p_j*i*(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_k})...(1-\frac{1}{p_k})=p_j*φ(i) φ(pji)=pji(1p11)(1p21)...(1pk1)...(1pk1)=pjφ(i)

i%primes[j]!=0

φ ( i ) = i ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) . . . ( 1 − 1 p k ) φ(i)=i(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_k}) φ(i)=i(1p11)(1p21)...(1pk1)
φ ( p j ∗ i ) = p j ∗ i ∗ ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) . . . ( 1 − 1 p k ) ( 1 − 1 p j ) = p j ∗ φ ( i ) ∗ ( 1 − 1 p j ) = φ ( i ) ∗ ( p j − 1 ) φ(p_j*i)=p_j*i*(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_k})(1-\frac{1}{p_j})=p_j*φ(i)*(1-\frac{1}{p_j})=φ(i)*(p_j-1) φ(pji)=pji(1p11)(1p21)...(1pk1)(1pj1)=pjφ(i)(1pj1)=φ(i)(pj1)

#include <iostream> #include <algorithm> #include <unordered_map> #include <vector> const int maxn=1e6+5; const int mod=1e9+7; using namespace std; int primes[maxn],euler[maxn],cnt; bool st[maxn]; void get_eulers(int n) { 
    euler[1]=1; for(int i=2;i<=n;i++) { 
    if(!st[i]) { 
    primes[cnt++]=i; euler[i]=i-1;//质数的欧拉函数除掉它本身 } for(int j=0;primes[j]<=n/i;j++) { 
    int k=primes[j]*i; st[k]=true; if(i%primes[j]==0) { 
    euler[k]=euler[i]*primes[j]; break; } euler[k]=euler[i]*(primes[j]-1); } } } int main() { 
    int n; cin>>n; get_eulers(n); long long sum=0; for(int i=1;i<=n;i++) sum+=euler[i]; cout<<sum<<endl; return 0; } 
3.欧拉定理

若a和n互质,则有 a φ ( n ) ≡ 1   m o d   n a^{φ(n)}≡1\ mod\ n aφ(n)1 mod n
eg. a=5,n=6,则φ(6)=2, a φ ( n ) = 25 a^{φ(n)}=25 aφ(n)=25,25%6=1,1%6=1

简要证明

假设1~n中,与n互质 的数有 a 1 , a 2 , . . . a φ ( n ) a_1,a_2,...a_{φ(n)} a1,a2,...aφ(n)
因为a和n互质,提出a, a φ ( n ) ( a 1 , a 2 , . . . a φ ( n ) ) ≡ a 1 , a 2 , . . . a φ ( n )   ( m o d   n ) a^{φ(n)}(a_1,a_2,...a_{φ(n)})≡a_1,a_2,...a_{φ(n)}\ (mod\ n) aφ(n)(a1,a2,...aφ(n))a1,a2,...aφ(n) (mod n)

        ——> a φ ( n ) ≡ 1   ( m o d   n ) a^{φ(n)}≡1\ (mod\ n) aφ(n)1 (mod n)

所以 a a 1 , a a 2 , . . . a a φ ( n ) aa_1,aa_2,...aa_{φ(n)} aa1,aa2,...aaφ(n)在mod n的情况下和上述 a 1 , a 2 , . . . a φ ( n ) a_1,a_2,...a_{φ(n)} a1,a2,...aφ(n)在mod n的情况下是相等的

4.费马小定理

若p为质数,且由欧拉定理有 a φ ( p ) ≡ 1   m o d   p a^{φ(p)}≡1\ mod\ p aφ(p)1 mod p φ ( p ) = p − 1 φ(p)=p-1 φ(p)=p1=>费马定理

若正整数a与素数p互质,则有 a p − 1 ≡ 1 ( m o d   p ) a^{p-1}≡1(mod\ p) ap11(mod p),或写为 a p ≡ a ( m o d   p ) a^p≡a(mod\ p) apa(mod p)

费马小定理求逆元

若p为素数,且(a,p)=1, a p − 1 ≡ 1 ( m o d   p ) a^{p-1}≡1(mod\ p) ap11(mod p) a a p − 2 ≡ 1 ( m o d   p ) aa^{p-2}≡1(mod\ p) aap21(mod p),a的逆元就是 a p − 2 a^{p-2} ap2

欧拉定理求逆元

若a,p互素, a φ ( p ) ≡ 1 ( m o d   p ) a^{φ(p)}≡1(mod\ p) aφ(p)1(mod p) a a φ ( p ) − 1 ≡ 1 ( m o d   p ) aa^{φ(p)-1}≡1(mod\ p) aaφ(p)11(mod p)

5.快速幂

欧拉函数算法实现_编程代码 (https://mushiming.com/)  第3张

例题

欧拉函数算法实现_编程代码 (https://mushiming.com/)  第4张
计算范围可达到10^9

ak,朴素做法循环一遍,时间复杂度O(k)

int ans=1; for(int i=1;i<=k;i++) { 
    ans=ans*a%p; } 

如果用快速幂的话,logk->30左右

算法原理:

预处理出 a 2 0   m o d   p a^{2^0}\ mod\ p a20 mod p a 2 1   m o d   p a^{2^1}\ mod\ p a21 mod p,…, a 2 l o g k   m o d   p a^{2^{logk}}\ mod\ p a2logk mod p这logk个值

a k = a 2 i ∗ a 2 j ∗ a 2 m . . . = a 2 i + 2 j + 2 m + . . . a^k=a^{2^i}*a^{2^j}*a^{2^m...}=a^{2^i+2^j+2^m+...} ak=a2ia2ja2m...=a2i+2j+2m+...,就是要把指数拆成二进制表示。

a 2 0 a^{2^0} a20  ——>   a 1 = a a^1=a a1=a
a 2 1 a^{2^1} a21  ——>   = a 2 0 ∗ 2 = ( a 2 0 ) 2 =a^{2^0*2=(a^{2^0})^2} =a202=(a20)2
a 2 2 a^{2^2} a22  ——>   = a 2 1 ∗ 2 = ( a 2 1 ) 2 =a^{2^1*2=(a^{2^1})^2} =a212=(a21)2

a 2 l o g k a^{2^{logk}} a2logk  ——>   a 2 l o g k − 1 ∗ 2 = ( a 2 l o g k − 1 ) 2 a^{2^{logk-1}*2=(a^{2^{logk-1}})^2} a2logk12=(a2logk1)2

eg. 4 5   m o d   10 4^5\ mod\ 10 45 mod 10

4 2 0 %   10 = 4 4^{2^0}\%\ 10=4 420% 10=4
4 2 1 %   10 = 6 4^{2^1}\%\ 10=6 421% 10=6
4 2 2 %   10 = 6 4^{2^2}\%\ 10=6 422% 10=6

4 5 = 4 ( 101 ) 2 = 4 2 0 + 2 2 = 4 2 0 ∗ 4 2 2 = ( 4 ∗ 6 ) %   10 = 4   ( % 10 ) 4^5=4^{(101)_2}=4^{2^0+2^2}=4^{2^0}*4^{2^2}=(4*6)\%\ 10=4\ (\%10) 45=4(101)2=420+22=420422=(46)% 10=4 (%10)

时间复杂度分析:

预处理部分logk个数,拆分成的二进制表示这步也是logk个,所以总的时间复杂度就是O(logk)

#include <iostream> #include <algorithm> #include <unordered_map> #include <vector> const int maxn=1e6+5; const int mod=1e9+7; typedef long long ll; using namespace std; long long quick_pow(int a,int b,int p) { 
    long long res=1%p;//一定要注意要这样初始化,因为当p=1时,结果就会不同了,1%1=0 while(b) { 
    if(b&1)//当前位数是1,就乘到结果上 res=res*1ll*a%p;//第一次就是a^{2^0} a=a*1ll*a%p;//平方一下 b>>=1;//右移一位 } return res; } int main() { 
    int n; scanf("%d",&n); while(n--)//数据量有点大,建议用scanf { 
    int a,k,p; scanf("%d%d%d",&a,&k,&p); printf("%lld\n",quick_pow(a,k,p)); } return 0; } 
5.快速幂求逆元
对于整数a,n,如果有gcd(a,n)=1,那么就存在一个整数x使得 ax=1(mod n),x即为a的逆元

欧拉函数算法实现_编程代码 (https://mushiming.com/)  第5张

性质

a b ≡ a ∗ b − 1 ( m o d   m ) \frac{a}{b}≡a*b^{-1}(mod\ m) baab1(mod m)

b ∗ a b ≡ b ∗ a ∗ b − 1 ( m o d   m ) b*\frac{a}{b}≡b*a*b^{-1}(mod\ m) bbabab1(mod m)

a ≡ b ∗ a ∗ b − 1 ( m o d   m ) a≡b*a*b^{-1}(mod\ m) abab1(mod m),又a和m互质,所以有

b ∗ b − 1 ≡ 1 ( m o d   m ) b*b^{-1}≡1(mod\ m) bb11(mod m)


eg. b=3,m=5,求b%m的逆元 2   (3*2)%5=1

逆元为 3 5 − 2 = 3 3 = 27 % 5 = 2 3^{5-2}=3^3=27\%5=2 352=33=27%5=2


例题

欧拉函数算法实现_编程代码 (https://mushiming.com/)  第6张

输入
3 4 3 8 5 6 3 
输出
1 2 impossible 
费马小定理求逆元

若p为素数,且a,p互质, a p − 1 ≡ 1 ( m o d   p ) a^{p-1}≡1(mod\ p) ap11(mod p) a a p − 2 ≡ 1 ( m o d   p ) aa^{p-2}≡1(mod\ p) aap21(mod p),a的逆元就是 a p − 2 a^{p-2} ap2

#include <iostream> #include <algorithm> #include <unordered_map> #include <vector> const int maxn=1e6+5; const int mod=1e9+7; typedef long long ll; using namespace std; ll quick_pow(int a,int b,int p) { 
    ll res=1%p;//一定要注意要这样初始化,因为当p=1时,结果就会不同了,1%1=0 while(b) { 
    if(b&1) res=res*1ll*a%p;//第一次就是a^{2^0} a=a*1ll*a%p;//平方一下 b>>=1;//右移一位 } return res; } int main() { 
    int n; scanf("%d",&n); while(n--)//数据量有点大,建议用scanf { 
    int a,p; scanf("%d%d",&a,&p); if(a%p!=0) printf("%lld\n",quick_pow(a,p-2,p)); else printf("impossible\n"); } return 0; } 
a是p的倍数的时候是无解的 a*x%p=0,不可能模p等于1

由于a不是p的倍数,且p是质数,所以a和p一定互质,由费马定理 b p − 1 % p ≡ 1 b^{p-1}\%p≡1 bp1%p1知逆元一定存在。


int ans=quick_pow(a,p-2,p); if(ans) printf("%lld\n",quick_pow(a,p-2,p)); else printf("impossible\n"); 

不能这样判断,因为输入数据为2 2,2%2=0逆元应该不存在,但是这里2^0=1,好像没有次幂会为0吧,或许当底数为0的时候…

THE END

发表回复