数论入门2——gcd,lcm,exGCD,欧拉定理,乘法逆元,(ex)CRT,(ex)BSGS,(ex)Lucas,原根,Miller-Rabin,Pollard-Rho

数论入门2

另一种类型的数论...

GCD,LCM

定义\(gcd(a,b)\)为a和b的最大公约数,\(lcm(a,b)\)为a和b的最小公倍数,则有:

将a和b分解质因数为\(a=p1^{a1}p2^{a2}p3^{a3}...pn^{an},b=p1^{b1}p2^{b2}p3^{b3}...pn^{bn}\),那么\(gcd(a,b)=\prod_{i=1}^{n}pi^{min(ai,bi)},lcm(a,b)=\prod_{i=1}^{n}pi^{max(ai,bi)}\)(0和任何数的最大公约数是这个数,最小公倍数是0)

显然成立的吧。

所以我们有\(gcd(a,b)*lcm(a,b)=ab\)

\(gcd(a,b)\)的方法:

首先我们有:\(gcd(a,b)=gcd(b,a-b)(a>=b)\)

显然的啊,两个数相减怎么可能影响他们的最大公约数,毕竟是“公”约数,相减肯定还存在这个因子啊。

所以拓展一下我们就有:\(gcd(a,b)=gcd(b,a \% b)(a>=b)\)

取模就是不断相减,所以也是成立的呀。

所以我们可以写出简洁的代码:

int gcd(int a,int b){return !b?a:gcd(b,a%b);}

很显然这个复杂度是log的,每次至少减少一半。

lcm的话,\(a/gcd(a,b)*b\)即可。

然后呢,gcd和lcm具有交换律和结合律:

\(gcd(a,b,c)=gcd(gcd(a,b),c)=gcd(c,b,a)\)

所以求多个数的gcd或lcm的时候就可以递推求啦。

还有一个东西就是\(gcd(ka,kb)=k*gcd(a,b)\),也是显然的。

exGCD

对于方程\(ax+by=c\),显然若\(gcd(a,b)|c\)的时候才有解。

因为不整除的话\(ax+by\)肯定有\(c\)不包含的因子,那样的话怎么可能有解呢?

然后呢,我们可以通过这样的方式快速借助求gcd的过程求出\(ax+by=gcd(a,b)\)的一组解。

\(a=gcd(a,b),b=0\)时,显然\(x=1,y=0\)

\(a = b, b = a \% b\),则有方程\(b *x1 +(a \% b) * y1 = gcd(b, a \% b)\)

又因为\(gcd(a, b) = gcd(b, a \% b)\),且\(a \% b = a - b * ⌊a / b⌋\)

\(b * x1 + (a - b * ⌊a / b⌋) * y1 =gcd(a, b)\)

整理得:\(a * y1 +b * (x1 - ⌊a / b⌋ *y1) = gcd(a, b)\)

所以原方程中:\(x = y1, y = x1 - ⌊a / b⌋ *y1\)。于是我们只要递归求出\(x1, y1\)就能求出\(x, y\)

我们现在已经求得了\(ax +by = gcd(a, b)\)的解,那么对于方程\(ax + by = c (gcd(a, b) | c)\)呢?

因为已经知道\(a *x1 +b * y1 = gcd(a, b)\)的解\(x1, y1\),左右两边同乘以\(c / gcd(a, b)\) 得:

\(a * x1 * c / gcd(a, b) +b * y1 * c / gcd(a, b) = c\)

则原方程的一组解\(,x2 = x1 * c / gcd(a, b), y2 = y1 * c / gcd(a, b)\)

由此得出解集\(,{(x, y) | x = x2 + k * b / gcd(a, b), y = y2 - k * a / gcd(a, b), k ∈ Z}\)

对于线性同余方程\(ax\equiv c\quad(mod\quad b)\),我们怎么做呢?

显然可以转化成\(ax+by=c\)的形式,然后直接用扩欧解决啦。

贴个代码:

ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b){x=1;y=0;return a;}
    ll g=exgcd(b,a%b,y,x);y-=a/b*x;
    return g;
}

欧拉定理

费马小定理:

若p是质数,且\(gcd(a,p)=1\),则\(a^{p-1}\equiv1(mod\quad p)\)

欧拉定理:

\(gcd(a,p)=1\),则\(a^{\varphi(p)}\equiv1(nod \quad p)\)

扩展欧拉定理:

\(a^b\equiv \begin{cases} a^{b\%\varphi(p)}~~~~~~~~~~~gcd(a,p)=1\\ a^b~~~~~~~~~~~~~~~~~~gcd(a,p)\neq1,b<\varphi(p)\\ a^{b\%\varphi(p)+\varphi(p)}~~~~gcd(a,p)\neq1,b\geq\varphi(p) \end{cases}~~~~~~~(mod~p)\)

\(\varphi\)为欧拉函数。

证明:欧拉定理和扩展欧拉定理的证明

有个作用就是在算\(a^b\)的时候对b进行取模,使运算更高效,具体见luogu欧拉定理模板题

乘法逆元

\(gcd(a,b)=1,\)\(ax\equiv 1(mod~p)\),则称x为a在模p意义下的乘法逆元。

那么求法就很显然了。

欧拉定理:a在模p意义下的逆元为\(a^{\varphi(p)-1}\)

exGCD:当做一个同余方程求,但是要通过+kp的方式变为一个正数。

此外还有一个线性求逆元的算法。

首先,1在任何模数下的逆元都是1。

然后设\(p=i*k+r,r<i,1<i<p\)

那么我们有\(i*k+r\equiv 0(mod~p)\)

两边同时乘上\(i^{-1}*r^{-1}\)得:

\(k*r^{-1}+i^{-1}\equiv 0(mod~p)\)

\(i^{-1}\equiv -k* r^{-1}(mod~p)\)

因为\(k=\lfloor p/i\rfloor,r=p\%i\),所以我们得到了逆元的递推式:

a[i]=-(p/i)*a[p%i]%p;

以及提一句,逆元是完全积性函数,从定义式里面就能看出来。

对于一个前缀积的序列的话,我们可以求出最后一项的逆元,然后\(O(n)\)递推回去,实际上逆元可以理解为模意义下的\(\frac{1}{x}\),怎么递推的话也很显然了,以阶乘为例:

fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%P;
ifa[n]=ksm(fac[n],P-2);
for(int i=n-1;~i;i--) ifa[i]=ifa[i+1]*(i+1)%P;

还有就是:

\((a/b)\%p=(a\%(bp))/ p\)

证明:

\((a/b)\%p=a/b-⌊(a/b)/p⌋*p\)

\(=a/b-⌊a/(b*p)⌋*p\)

\(=a/b-⌊a/(bp)⌋*b*p/b\)

\(=(a\%(bp) )/p\)

CRT

已知系数全部为1的线性同余方程组:

\(x \equiv a_i(mod ~p_i)\),其中\(p_i\)两两互质,求x的最小非负整数解。

那么我们考虑这样一个思路:

从1到n考虑每个同余方程组,我们设\(M=\prod_{i=1}^{n}p_i\),那么当我们考虑到第i个方程的时候,我们设\(T=\frac{M}{p_i}\),那么我们发现把第i个方程的解\(x\)加上\(kT\)的话,\(x+kT\)带到别的同余方程组的余数不会改变。

假设我们已经求出了前\(i-1\)个方程的通解\(x\),求出了第\(i\)个方程的一个解\(x_i\),那么显然,\(Tx_i+x\)是前\(i\)个方程的解。

我们用exGCD求出第i个线性同余方程组\(Tx_i\equiv a_i(mod~p_i)\)的解,一步步合并即可,因为\(p_i\)显然和\(T\)互质,所以实际上是求出逆元之后乘上\(a_i\)

exCRT

已知系数全部为1的线性同余方程组:

\(x \equiv a_i(mod ~p_i)\),其中\(p_i\)都是正整数,求x的最小非负整数解。

上面的算法失效了。为什么?都不一定互质了怎么可能还去求逆元?

所以我们要考虑一个新的思路:

假设我们已经求出了前\(i-1\)个方程的通解\(x\),设\(M\)为前i-1个数的lcm,那么我们发现\(x+kM\)如果能够满足第\(i\)个方程,那么前i个方程就都解出来啦。

我们写个式子:\(x+kM \equiv a_i(mod~p_i)\),移项得\(kM \equiv a_i-x(mod ~p_i)\)

那么我们解出来k之后,前i个方程的通解就出来啦,一步步做下去即可。

贴个代码:

ll mul(ll x,ll y,ll p){ll g=0;for(;y;y>>=1,x=(x+x)%p) if(y&1) g=(g+x)%p;return g;}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b){x=1;y=0;return a;}
    ll g=exgcd(b,a%b,y,x);y-=a/b*x;
    return g;
}
for(int i=1;i<=n;i++) scanf("%lld%lld",&b[i],&a[i]);
ans=a[1];M=b[1];
for(int i=2;i<=n;i++)
{
    ll A=M,B=b[i],c=(a[i]%B-ans%B+B)%B,x,y;
    ll gcd=exgcd(A,B,x,y),d=B/gcd;
    if(c%gcd) return 0;x=mul(x,c/gcd,d);
    ans+=x*M;M*=d;ans=(ans%M+M)%M;
}

注意一下,因为是求lcm,所以那个B要除以gcd。

BSGS

exBSGS

原根

Lucas

exLucas

Miller-Rabin

Pollard-Rho

猜你喜欢

转载自www.cnblogs.com/CK6100LGEV2/p/10352130.html