【經常使用算法總結——逆元】

乘法逆元,通常是用來求html

的值,p一般爲質數c++

定義

a*x1(mod b),且a與b互質,咱們定義x是a的逆元,記爲a^(-1),因此也能夠說x是a在mod b意義下的倒數算法

因此對於a/b(mod p),咱們能夠先求出b在mod p下的逆元,而後乘a再mod p就是這個分數的值了spa

逆元求法

  首先看到同餘方程,這個就是典型的求一個數模p下的逆元,而對於逆元的求法,咱們有多種操做:3d

擴展歐幾里得

  首先,這個算法的性質以下rest

擴展歐幾里德算法是用來在已知a, b求解一組x,y,使它們知足貝祖等式: ax+by = gcd(a, b) =d(解必定存在,根據數論中的相關定理)。擴展歐幾里德經常使用在求解模線性方程及方程組中。

  在這道題中,咱們能夠把ax≡1(mod b)轉化成ax+by=1,只不過y多是負數,然而與擴歐公式仍是有差異,但不可貴出,gcd(a,b)=1,code

  推導公式以下htm

由最大公因數的定義,可知 a 是 gcd(a,b) 的倍數,且 b 是 gcd(a,b) 的倍數, 若 x,y 都是整數,就肯定了 ax + by 是 gcd(a,b) 的倍數, 由於 m = ax + by因此 m 必須是 gcd(a,b) 的倍數, 那麼 m \mod gcd(a,b) = 0

..................................................blog

而後根據一系列推導就得出了具體公式:具體請見同餘方程第一篇題解<<<<大佬。ci

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 long long x,y;
 4 void exgcd(long long a,long long b)
 5 {
 6     if(b==0)
 7     {
 8         x=1;
 9         y=0;
10         return;
11     }
12     exgcd(b,a%b);
13     long long z=x;
14     x=y;
15     y=z-(a/b)*y;
16 }
17 int main()
18 {
19     long long a,b;
20     cin>>a>>b;
21     exgcd(a,b);
22     while(x<0)
23         x+=b; 
24     x%=b;
25     cout<<x;
26     return 0;
27 }

快速冪

  這個作法運用到了費馬小定理

若p爲素數,a爲正整數,且a、p互質。 則有a^(p-1)≡1(mod p)。

而後代入原式,神奇的事發生了

a*x≡1(mod p) a*x=a^(p-1) (mod p) x=a^(p-2) (mod p)

而後咱們求a^(p-2)(mod p)就是它的逆元啦

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 ll n,p;
 5 int fpm(ll x,ll y)//快速冪
 6 {
 7     x%=p;
 8     ll ans=1;
 9     while(y)
10     {
11         if(y&1)ans=(ans*x)%p;
12         y>>=1;
13         x=x*x%p;
14     }
15     return ans;
16 }
17 int main()
18 {
19     cin>>n>>p;
20     for(int i=1;i<=n;i++)
21     {
22         printf("%lld\n",fpm(i,p-2));
23     }
24 }

 線性算法

  以上算法針對於求單個逆元,可是有一長串的時候,你就TLE了,因此,聰明的大佬們研發的線性算法出現了。

  設x的逆元爲x^(-1)

  咱們先有一個1的逆元爲1

       設p=k*i+r,(1<r<i<p) 也就是 k 是 p / i的商,r是餘數 。

  

而後乘上i的逆元和r的逆元

而後公式就出來了

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 ll n,p;
 5 ll inv[3000005];
 6 int main()
 7 {
 8     cin>>n>>p;
 9     inv[1]=1;
10     printf("%lld\n",inv[1]);
11     for(int i=2;i<=n;i++)
12     {
13         inv[i]=(p-p/i)*inv[p%i]%p;
14         printf("%lld\n",inv[i]);
15     }
16 }

 

學會了求逆元后,咱們就能夠學學其餘interesting的東西——中國剩餘定理

相關文章
相關標籤/搜索