乘法逆元小结
在求解除法取模問題(a/b)%m時,我們可以轉化為(a%(b?m))/b,
但是如果b很大,則會出現爆精度問題,所以我們避免使用除法直接計算。
可以使用逆元將除法轉換為乘法:
假設b存在乘法逆元,即與m互質(充要條件)。設c是b的逆元,即b?c≡1(modm),那么有a/b=(a/b)?1=(a/b)?b?c=a?c(modm)
即,除以一個數取模等于乘以這個數的逆元取模。
擴展歐幾里得算法:
要求a,m互素。存在唯一解。
之前總結過擴展歐幾里得算法
代碼:
int extgcd(int a, int b, int& x, int& y) {int d = a;if(b != 0){d = extgcd(b, a % b, y, x);y -= (a / b) * x;}else {x = 1;y = 0;}return d; } int mod_inverse(int a, int m) {int x, y;extgcd(a, m, x, y);return (m + x % m) % m; }費馬小定理:
在p是素數的情況下,對任意整數x都有xp≡x(mod)p。
如果x無法被p整除,則有xp?1≡1(modp)。
可以在p為素數的情況下求出一個數的逆元,x?xp?2≡1(modp),xp?2即為逆元。
代碼:
利用快速冪求出逆元。歐拉函數:
令?(m)表示小于等于m且與m互素的正整數的個數。
如果x和m互質,則有x?(m)≡1(modm),即x×x?(m)?1≡1(modm),x?(m)?1即為x的逆元。
在m為質數的情況下,?(m)=m?1,即為費馬小定理。
代碼:
關鍵是求出歐拉函數的值。
利用歐拉函數的積性性質:
對于任意整數n,可以將它分解n=pk11?pk22?pk33...pkmm,其中pi為質數。
其中?(n)=?(p1k1)??(pk22)...?(pkmm)
最后轉化為?(n)=n?∏(pi?1)/pi
對給定n進行整數分解。時間復雜度O(n??√)。
int eurler_phi(int n) {int res = n;for(int i = 2; i * i <= n; i++){if(n % i == 0){res = res / i * (i - 1);while(n % i == 0) n /= i;}}if(n != 1) res = res / n * (n - 1);return res; }篩法求歐拉函數值的表,利用埃氏篩法,每次發現質因子就把他的倍數的歐拉函數乘上(p?1)?p。時間復雜度O(maxn)。
如ACdreamers博客里介紹,利用定理進行優化:
當n為奇數時,有?(2n)=?(n)
因為2n是偶數,偶數與偶數一定不互素,所以只考慮2n與小于它的奇數互素的情況,則恰好就等于n的歐拉函數值。
int euler[maxn]; void euler_phi2() {for(int i = 1; i < maxn; i++){if(i % 2 == 0) euler[i] = i / 2;else euler[i] = i;}for(int i = 3; i < maxn; i += 2){if(euler[i] == i){for(int j = i; j < maxn; j += i){euler[j] = euler[j] / i * (i - 1);}}} }線性時間求所有逆元:
規定p為質數,且1?1≡1(modp)
設p=k?a+b,b<a,1<a<p,即k?a+b≡0(modp)
兩邊同時乘以a?1?b?1,得到
k?b?1+a?1≡0(modp)
a?1≡?k?b?1(modp)
a?1≡?p/a?(pmoda)?1(modp)
從頭開始掃一遍即可,時間復雜度O(n)
代碼:
int inv[maxn]; inv[1] = 1; for(int i = 2; i < maxn; i++)inv[i] = (p - p / i) % p * inv[p % i];轉載于:https://www.cnblogs.com/Tuesdayzz/p/5758670.html
總結
- 上一篇: pppcloud云主机内LINUX用户安
- 下一篇: Openstack的RPC通信代码调用架