數論倒數,又稱逆元
取模
對於取模,有一下一些性質:
但是唯獨除法是不滿足的:
為什么除法錯的呢?很好證明:
而對於一些題目,我們必須在中間過程中進行求余,否則數字太大,電腦存不下,那如果這個算式中出現除法,我們就需要逆元了。
逆元
定義:
我們知道,如果a*x = 1,那么x是a的倒數,x = 1/a
而在數論問題中,大部分情況都有取模,所以問題就變成了:
這時x在數值上就不一定等於我們常規意義上的1/a了,我們可以理解為要求在0,1,2……p-1之間找一個數,是的這個數和a相乘后再取模p,得到的結果為1。
現在就要在回到剛才的問題了,除以一個數等於乘上這個數的倒數,在除法取余的情況下,就是乘上這個數的逆元,即:
這樣就把除法,完全轉換為乘法了。
逆元的求解
對於逆元的求解,如果n較小的話,是容易算出來的,例如,求3在模26下的逆元:
但是當n非常大的時候,就需要引入一個算法來計算
(1)擴展歐幾里得算法(extend_gcd)
對於逆元的表達式可以做一些變換:
當gcd(a,n)=1時,代入extend_gcd(a,n,x,y),得到的非負的x值,就是a對模n的逆元。
算法實現與證明
也就是說,我們得到了一個和gcd算法中,gcd(m,n)=gcd(n,m%n)相似的恆等式
什么意思呢?舉個例子,就是
如果想要x為正值,根據
只再做一步:
if (x < 0) { x += b; y -= a; }
擴展:
完整算法:
int extend_gcd(int a, int b, int& x, int& y) { if (b == 0) { x = 1, y = 0; return a; } int q = extend_gcd(b, a % b, x, y); int temp = x; x = y; y = temp - a / b * y; return q; }
(2)費馬小定理
如果p是一個質數,並且gcd(a,p)=1
兩邊同除以 a
所以
用快速冪求一下,復雜度O(logn)
(3)不知道叫啥
當p為質數時有
證明:
寫成算法就是一個遞歸,到1為止,因為1的逆元就是1
int inv(int t, int p) { return t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p; }
這個方法復雜度是O(n),但並不是說比前兩個差,它可以在O(n)的復雜度內算出n個數的逆元,上面的算法加一個記憶性搜索就行了
int inv(int t, int p) { return INV[t] = t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p; }