欧拉函数相关问题代码实现

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)。

例题

在这里插入图片描述

输入
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),所以要用筛法。

例题

在这里插入图片描述

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.快速幂

在这里插入图片描述

例题

在这里插入图片描述
计算范围可达到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的逆元

在这里插入图片描述

性质

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


例题

在这里插入图片描述

输入
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的时候…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值