對於取餘運算,有一下一些性質:html
可是惟獨除法是不知足的:算法
爲何除法錯的呢?很好證實:測試
而對於一些題目,咱們必須在中間過程當中進行求餘,不然數字太大,電腦存不下,那若是這個算式中出現除法,咱們就須要逆元了,將除法運算轉換爲乘法運算。ui
對於c,能夠說是特殊意義上的倒數,咱們能夠理解爲要求在0,1,2……p-1之間找一個數,是的這個數和a相乘後再取模p,獲得的結果爲1。spa
如今就要在回到剛纔的問題了,除以一個數等於乘上這個數的倒數,在除法取餘的狀況下,就是乘上這個數的逆元,即:3d
這樣就把除法,徹底轉換爲乘法了。code
惟一性htm
給定一個數a,若存在模p下的逆元c,c必定惟一。blog
自反性class
c是a的逆元,a也是c的逆元。
對於逆元的求解,若是n較小的話,是容易算出來的,例如,求3在模26下的逆元:
可是當n很是大的時候,手動求解就很是困難了。
$a\cdot a^{1}\equiv 1(mod\ b)$
對於逆元的表達式能夠作一些變換:
當gcd(a,b)=1時,代入extend_gcd(a,b,x,y),獲得的非負的x值,就是上面的$a^-1$
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; }
只適用於模數爲質數的狀況,若是模數不是質數,能夠變換一下,用歐拉定理。
若是p是一個質數,且a不是p的倍數則有
$a^{p-1}\equiv 1(mod\ p)$
根據同餘除法定理,兩邊同除以a
$a^{p-2}\equiv a^{-1}(mod\ p)$
因此
$a^{-1}= a^{p-2}(mod\ p)$
用快速冪求一下,複雜度O(logn)
當p爲質數時有$$a^{-1}=(p-[p/a])\cdot (p\%a)^{-1}\%p$$
1的逆元就是1,這個方法複雜度是$O(n)$,但並非說比前兩個差,它能夠在O(n)的複雜度內算出n個數的逆元。
inv[1] = 1; for(int i = 2; i < p; ++ i) inv[i] = (p - p / i) * inv[p % i] % p;
設 $f(i)=inv(i\ !)$
則根據:$f(i-1)=\frac{1}{\ (i-1)\ !}=\frac{1}{i\ !}\times i =f(i)\times i$
有:$f(i-1) = f(i)\times i$
假設要求 $[1,n]$ 中全部數的逆元,先求得 $[1,n]$ 中全部數的階乘
能夠用 費馬小定理 求得 $f(n)$ 的值,以後遞推出 $f(1 \sim n)$ 的值
可是 $inv(1! \sim n! )$ 並非咱們想要的答案,須要繼續轉化。
根據:$inv(i)=\frac{1}{i}=\frac{1}{i\ !}\times (i-1)\ ! = inv(i!)\times (i-1)!$
最終的轉換式 :$$inv(i) = inv(i!) \times(i-1)\ ! $$
時間複雜度也是$O(n)$
int N,P; fact[0] = 1; //mod P 求階乘 for (int i = 1; i <= N; i++) fact[i] = fact[i - 1] * i % P; //求N! mod p的逆元 inv[N] = quickPower(fact[N], P - 2, P) % P; //遞推求N!~1! mod p的逆元 for (int i = N - 1; i >= 1; i--) inv[i] = inv[i + 1] * (i + 1) % P; //轉換輸出 for (int i = 1; i <= N; i++) printf("%d\n", (inv[i] * fact[i - 1]) % P);
費這麼大周章求出來的逆元究竟有什麼用呢?
已知$n$爲任意整數,$(a,p)=1$,則$n\div a\ mod\ p = n\cdot a^{-1}\ mod\ p$,
好比 $12\div 4\ mod\ 7 = 12\cdot 2\ mod\ 7=3$。
具體使用狀況就能夠是上面提到的,一個取餘運算式中間出現了除號。
但若是式子中沒有取餘呢?那本身取一個唄,取一個特別大的素數(可是不能太大,推薦取1e9+7,好記,快速冪也不會爆long long範圍,能夠運算全部int範圍的數據)
測試代碼:
const LL P = 1e9 + 7; LL quickPower(LL a, LL n, LL p) { LL res = 1; while (n) { if (n & 1) { res = (res % p * a % p) % p; } a = (a % p * a % p) % p; n >>= 1; } return res; } int main() { LL _a = quickPower(8, P - 2, P); LL res = (72 * _a) % P; cout << res << endl; return 0; }
注意如下幾點: