欧拉函数与逆元

欧拉函数

定义:一个正整数内(包含自身)所有与自身互质的数的个数.1与所有数互质,即phi[1]=1

几个式子

1.n为任意数,phi[n]=n*(1-1/n1)(1-1/n2)…(1-1/nn) n1到nn是n的质因子
2.如果n是一个质数 phi[n]=n-1;
3.如果n是一个奇数 phi[2n]=phi[n] (根据式子1得到)
4.如果gcd(n,m)=1,即是n与m互质,那么它们的质因子没有交集,根据1式子得到phi[n
m]=phi[n]phi[m]
5.如果n的质因子集是m的质因子集的子集,那么phi[n
m]=n*phi[m]

代码.欧拉筛

typedef long long ll;
const ll N=1e7+10;
ll phi[N],prime[N];
void Euler_sieve(ll n)
{
    ll t=0;
    for(ll i=2;i<=n;i++){
        if(phi[i]==0)
            phi[i]=i-1,prime[t++]=i; 	//式子2
        for(ll j=0;j<t&&prime[j]*i<=n;j++){
        	// 保证线性的
            if(i%prime[j]==0){
            	// 式子5
            	phi[i*prime[j]]=phi[i]*prime[j];
            	break;
        	}
        	// 式4
        	phi[i*prime[j]]=phi[i]*phi[prime[j]];
    }
}

代码.求解单个phi[i]

using ll=long long;
ll Euler(ll n)
{
	ll ans=n;
	for(ll i=2;i*i<=n;i++){
		if(n%i==0){
			// 除去n中所有的i因子
			while(n%i==0) n/=i;
			ans-=ans/i; //式子1
		}
	}
	// 防止i*i大于最后一个质因子,所以在处理一下
	if(n>1) ans-=ans/n;
	return ans;
}

欧拉定理

设a,p互质,则有
aphi[p]恒等于1%p

证明

设集合A为phi[p]里面所有与p互质的小于等于p的元素
那么设集合B为集合A中每个元素与a相乘的集合
可以证明B中集合的元素模上p的值是唯一的(任意两个元素相减得a(Ai-Aj),a与p互质,A中元素都小于等于p,相减后一定小于p,所以没有使任意两数相减模上p为0的两个数,所以唯一)
因为所有小于等于且与p互质的元素都在A中,那么集合B%p等于集合A
得 aphi[p]恒等于1%p

数论倒数(逆元)

定义:如果两个数a,b,它们的乘积模上p等于1,那么它们互为关于模数p的数论倒数
(a+b)%p=(a%p+b%p)%p
(a-b)%p=(a%p-b%p)%p 需要判断a与b的大小,如果a>b,结果要保证为正,即最后结果+p再%p,…
(a*p)%p=(a%p * b%p)%p
没有除法的

如何求解1/a关于p的逆元

费马小定理(p必须是一个质数)

因为ap-2恒等于1/a%p (p是质数)
所以可以转换成ap-2%p

ll inv1(ll a,ll mod)
{
    ll p=mod;
    mod-=2;
    ll ans=1;
    while(mod)
    {
        if(mod&1)
            ans=(ans*a)%p;
        a=(a*a)%p;
        mod>>=1;
    }
    return ans%p;
}

通过欧拉值(phi[p])求解(a与p互质)

aphi[p] 恒等于 1%p 得出 aphi[p]-1 恒等于 1/a%p

ll phi(ll n){
    ll ans=n;
    for(ll i=2;i*i<=n;i++){
        if(n%i==0){
            while(n%i==0) n/=i;
            ans-=ans/i;
        }
    }
    if(n>1) ans-=ans/n;
    return ans;
}
ll inv2(ll m,ll n){
    ll b=phi(n)-1,ans=1;
    while(b){
        if(b&1) ans=(ans*m)%n;
        m=(m*m)%n;
        b>>=1;
    }
    return ans;
}

扩展欧几里得

扩展欧几里得存在ax+by=gcd(a,b)
如果模数是b,被摸数是a,那么上式转换为a*x%b=gcd(a,b)%b,x/gcd(a,b)%b=1/a%b
所以可以转换为x/gcd(a,b)%b 因为gcd(a,b)==1(b是质数).所以就是x%b=1/a%b
如果x是个负值,需要加上一个p

ll x,y,g;
int in3(ll a,ll b)
{
    if(!b)
    {
        x=1,y=0,g=a;
        return x/g;
    }
    inv2(b,a%b);
    ll c=x;
    x=y,y=c-(a/b)*y;
    return x/g;
}

递推

设被摸数为a,模数为p,则x=p/a,y=p%a
(x*a+y)%p=0%p
y%p=(p-x)*a%p 移动时,保证两边的正负性一样
1/a%p=((p-x)*1/y)%p
1/a%p=(p-p/a)*1/(p%a))%p
求解1/a关于p的逆元, 就要求解1/(p%a)关于p的逆元
一直递归下去,直到为1,因为1的逆元就是1

void inv4(ll a,ll mod,ll num[])
{
    for(ll i=2; i<=a; i++)
        num[i]=((mod-mod/i)*num[mod%i])%mod; //num存的是逆元
}
发布了4 篇原创文章 · 获赞 4 · 访问量 192

猜你喜欢

转载自blog.csdn.net/hire_candidate/article/details/104856994