# Mod 取模 ###### tags: `Algorithm` ## 定理 ::: info * 可以比照等號計算(除了除法,必須除數k必須和模數m互質,gcd(k,m)=1) * ==重要卻基本 `a ≡ a%m `== a 在除了除法以外 a%m 做其他運算的結果≡ a ::: #### 例題(1) > **15^8001 X 22^4781 X 11^3 除以7的餘數為多少** ``` 15 ≡ 1 (mod 7) 22 ≡ 1 (mod 7) 11 ≡ 4 (mod 7) ``` 1.根據上面==餘的相乘性質的次方公式== ``` 15^8001 ≡ 1^8001 (mod 7) 22^4781 ≡ 1^4781 (mod 7) 11^3 ≡ 4^3 (mod 7) ``` 2.然後上面三條可以相乘 (根據上面==同餘的相乘性質==) ``` 15^8001 X 22^4781 X 11^11 ≡ 1^8001 X 1^4781 X 4^3 (mod 7) 15^8001 X 22^4781 X 11^11 ≡ 64 (mod 7) ``` 然後因為 `64 ≡ 1 (mod 7)` `15^8001 X 22^4781 X 11^3 ≡ 1 (mod 7)` >> **所以餘1** #### 例題(2) >*4793316+7890323 除以7 會餘多少?* 假設我們知道下面 ``` 4793316 ≡ 3 (mod 7) 7890323 ≡ 0 (mod 7) ``` 那根據同餘的加法性質,可以得出 >> 4793316+7890323 ≡ 3(mod 7) ## 取模方式 ### 一般取模運算: >(a^n) %m 我們可以改寫為(a^n)%m= ((a%m)^n)%m,即循環n次。 > 當 a<m(a=a%m) `a^n≡(((((((a)*a)%m)*a)%m)*a)%m` 缺點:低效,循環了n次。 ```cpp= int exp_mod(int a,int n,int m){ a = a%m; int temp = 1; while(n>0) { temp = (temp * a)%m; n--; } return temp; } ``` ### 快速冪取模: ```cpp int qpow( int a, int n, int m) { int res = 1; while (n>0) { if (n&1) res = (res*a)%m; a = (a*a)%m; n>>=1; } return res; } ``` ### 大數 mod ```cpp int bmod( string s, int m) { int res=0; for (int i=0,len=s.size(); i<len; i++) { res=(res*10)%m; res = (res+(s[i]-'0'))%m; } return res%m; } ``` ## 除法 模反元素 a/b 沒辦法變成 (a%m)/(b%m) 但可以將除法轉換成乘反元素,以簡化計算 * 單位元素 可以取消另一元素運算的元素 在mod運算下的單位元素是 1 * 反元素: 跟另一個元素做運算後會等於單位元素的元素 * 模反元素(取模下的乘法反元素 ->除法效果) a*b mod m = 1 * a/b mod m → a*k mod m (k為b的模m乘法逆元) ### 尋找反元素 #### 方法一 費馬小定理 (m為質數且a⊥m) ```cpp a^(m-1) ≡ 1 (mod m) a^(m-2) ≡ 1/a (mod m) ``` ::: info 逆元為 a^(m-2)。(都要求a和m互質) ::: 1. >例 **2^100 mod 13** ``` 2^12≡1 (mod 13) (2^12)^8 ≡ 1^8 (mod 13) (2^12)^8*2^4 ≡ 2^4 (mod 13) 2^100 ≡ 16 (mod 13) 2^100 ≡ 3 (mod 13) ``` #### 方法二 擴展歐幾裡得算法 [擴展歐幾裡得](https://hackmd.io/vWIyTsIVTvSgNGjYDQTEnA#%E6%93%B4%E5%B1%95%E6%AD%90%E5%B9%BE%E8%A3%A1%E5%BE%97%E7%AE%97%E6%B3%95) ``` a * x ≡ 1 (mod n) 若gcd(a,n) = 1 → a*x + n*y = 1+0 = gcd(a,n) = 1 (mod n) → 可以用extgcd()算 → 已知 a,n 求 x,y ``` ```cpp= int exgcd(int a, int b, int &x, int &y) { if(b == 0) { x = 1; // 设置b=0时的特殊解 y = 0; return a; } int ans = exgcd(b, a % b, x, y); int t = x; // 将x2, y2换算成x1, y1 x = y; y = t - a / b * y; return ans; } int invele(int a, int m) { int x, y; exgcd(a, m, x, y); if(m < 0) m = -m; int ans = x % m; if(ans <= 0) ans += m; return ans; } ``` >[https://ithelp.ithome.com.tw/articles/10205727](https://ithelp.ithome.com.tw/articles/10205727) >[https://ithelp.ithome.com.tw/articles/10205906?sc=iThelpR](https://ithelp.ithome.com.tw/articles/10205906?sc=iThelpR) >[https://blog.csdn.net/acvay/article/details/47397977](https://blog.csdn.net/acvay/article/details/47397977)