標籤:
1) 素數
基礎理論
Theorem 素數有無限多個
Proof. 若存在最大素數P_max , 設 X = (P_1 * P_2 …… * P_max) + 1,此時如果X為素數, 則X > P_max, 矛盾, 如果X為合數, 則必存在它的一個素因子 P_X 且 P_X > P_max, 矛盾。
Theorem
顯然是在越大的範圍內分布得越稀疏的。
素數的判定
暴力的方法即枚舉每個小於n的數位約數, 簡單的最佳化即 枚舉 2~sqrt(n) 的所有質數。
由於一些偽素數令費馬小定理的逆定理不成立, 而去背一張偽素數表又不是很優美, miller-rabin 演算法隨之產生。
///////////////////////////////////////////////////////////////////
強偽素數
設n是一個大於4的奇整數,s和t是使得(n-1)=2^s*t的正整數,其中t為奇數,設B(n)是如下定義的整數集合:
a屬於集合B(n)若且唯若2≤a≤n-2且
1: a^tmodn=1
2: 存在整數i,0<=i<s滿足a^((2^i)*t) mod n=n-1
當n為素數時, 任意a在2和n-1中,均有a屬於集合B(n)
當n為合數時,若a屬於集合B(n),則稱n為一個以a為底(基)的強偽素數,稱a為n素性的強偽證據。
n為素數,說明它對所有底均為強偽素數
Btest(a,n){
//n為奇數,返回true。即返回真說明n是強偽素數
s←0; t ←n-1; //t開始為偶數
repeat
s++;t ← t÷2;
until t mod 2 = 1; //n-1=2st t為奇數
x ←at mod n;
if x=1 or x=n-1 then return true; //滿足①or②。
for i ←1 to s-1 do{
x ←x2 mod n;
if x=n-1 then return true; //滿足②,
}
return false;}
通過這一定義則發現,小於1000的奇合數中,隨機選到一個強偽證據的機率小於1%
更重要的是,對任一奇合數,強偽證據比例都很小
所以,我們可以多次運行下面的演算法,就可把錯誤機率降低我們可控制的範圍
MillRab(n) { //奇n>4,返回真時表示素數,假表示合數
a←uniform(2..n-2);
return Btest(a,n); //測試n是否為強偽素數
}//該演算法是3/4-正確,偏假的。
RepeatMillRob(n,k){
for i ←1 to k do
if MillRob(n) =false then
return false; //一定是合數
return true;
}
雖然miller-rabin 是一個隨機性演算法, 但是取前9個素數就可以保證在 10^18範圍內的正確性了。
代碼很簡單
#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>#define ll long longusing namespace std;int t, prim[15] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23};ll u;ll mypow(ll x, ll k, ll mod){ll ret = 1;while(k){if(k & 1) (ret *= x) %= mod;(x *= x) %= mod;k >>= 1;}return ret;}bool MR(ll a, ll n){if(a >= n) return 1;ll w = mypow(a, u, n);if(w == 1) return 1;for(int i = 0; i < t; i ++){if(w == n - 1) return 1;(w *= w) %= n;} return 0;}bool pd(ll n){if(n == 2) return 1;if(n < 2 || (!(n & 1))) return 0;t = 0; u = n - 1;while((u & 1) == 0) u >>= 1, t ++;for(int i = 1; i <= 9; i ++) if(!MR(prim[i], n)) return 0;return 1;}int main(){int N;while(scanf("%d", &N) != EOF){int cnt = 0;for(int i = 1; i <= N; i ++){ll x; scanf("%I64d", &x);if(pd(x)) cnt ++;}printf("%d\n", cnt);}return 0;}
篩法
求 不超過 n 的所有質數。
首先可以把每個數miller-rabin 判一下, 時間複雜度 O(nlogn)
考慮篩法。 直接的想法是每篩出一個質數, 就把它的倍數中小於n 的數標記為合數, 時間複雜度 O(nln(n))
在上面的篩法中, 一些還有多個質因子的素數會多次被篩掉, 產生了很多無用的花費。 如何減少冗餘? 直接的想法就是把每個合數標記且僅標記一次, 不妨用它最小的那個質因子去篩掉它。
於是有了一種最佳化的篩法: 枚舉每一個數, 枚舉之前篩出來過的質數, 把它們的乘積標記為合數, 如果當前數是當前質數的倍數, 則break掉。
這樣做每個合數只會被自己最小的質因子篩掉。因為兩者乘積的最小質因子要麼是枚舉的那個質數, 要麼是枚舉的整數的最小質因子,故在break之前篩掉的那些合數的最小質因子都是枚舉的那個質數, 但是如果不break的話,之後篩掉的那些合數的最小質因子就是整數的最小質因子而不是那個枚舉的質數了。
for(int i = 2; i <= n; i ++){if(!isprim[i]) prim[++ pp] = i;for(int j = 1; j <= pp && i * prim[j] <= n; j ++){isprim[i * prim[j]] = 1;if(i % prim[j] == 0) break;}}
2) 最大公約數
歐幾裡得演算法
gcd(a, b) = gcd(b, a % b)
若 m | a 且 m | b (a >= b),則 m | a - b, 則 m | a - k*b (k >= 0 且 k*b <= a)
求不定方程 ax + by = m 的解。
如果 m 不是 gcd(a, b) 的倍數, 那麼原方程顯然不存在整數解。
故問題轉化為 ax + by = gcd(a, b)
設 ax1 + by1 = gcd(a, b); bx2 + (a % b)y2 = gcd(b, a % b)
-> ax1 + by1 = bx2 + (a%b)y2 (歐幾裡得原理)
下面我們要把原式中的求模去掉以獲得 x1, y1 的值。
-> ax1 + by1 = bx2 + (a - [a / b] * b) y2
= ay2 + b(x2 - [a / b] y2)
所以我們可以通過遞迴得到 x1, y1 的一組解, 邊界是 b = 0 時, 這時原式退化成了一個一元方程, 顯然有 x = 1, y = 0
代碼
int exgcd(int a, int b, int &x, int &y){if(!b){x = 1, y = 0; return a;}int r = exgcd(b, a % b, x, y), t = x;x = y, y = t - (a / b) * y;return r;}
設x0, y0 是不定方程 ax + by = m 的一組解, (a, b) = g, 那麼解集為
{(x0 + b * t / g, y0 + a * t / g) : t ∈ z}
這樣就可以獲得需要任意解了。
線性同餘方程組
如果m1, m2 …… mn 兩兩互質, 可以使用中國剩餘定理。
中國剩餘定理說明:假設整數m1,m2, ... ,mn兩兩互質,則對任意的整數:a1,a2, ... ,an,方程組有解,並且通解可以用如下方式構造得到:
設是整數
m1,
m2, ... ,
mn的乘積,並設是除了
mi以外的
n- 1個整數的乘積。設為模的數論倒數方程組的通解形式為在模M的意義下, 方程組(S) 只有一個解:
這個定理的正確性是很顯然的。 對於原式中的每一項i, 在模 mi 的意義下都只有它自己這一項貢獻了 ai * ti * mi , 即 ai * 1, 其它項都是 mi 的倍數。 所以每個式子模 mi 都會是 ai.
當然在 m1 …… mn 不滿足兩兩互質是中國剩餘定理是不能用的, 連逆元都求不出來。
還有一種更直接的方法, 不過複雜度是 O(logn) 的。
考慮前兩個式子 x ≡ a1 (mod m1) 和 x ≡ a2 (mod m2)
可以寫成 m1 * k1 + a1 = x , m2 * k2 + a2 = x
即 m1 * k1 + a1 = m2 * k2 + a2
即 m1 * k1 - m2 * k2 = a2 - a1
設 g = gcd(m1, m2), l = lcm(m1, m2)
m1 * k1 / g - m2 * k2 / g = (a2 - a1) / g
這時候就可以用擴歐 求出 k1, k2 的一組解, 然後把k1帶回到原式中, 這樣這個同餘方程組就合并成了這樣一個同餘方程:
x ≡ a1 + m1 * k1 (mod lcm(m1, m2))
逆元
設正整數 m,對於任意正整數 a 滿足 (a , m ) = 1,總存在惟一的 b 滿足a × b ≡ 1
(mod m ) 且 1 ≤ b < m,稱 b 為模 m 意義下的逆元。
逆元應用在模意義下的整數除法中。
逆元的計算主要是兩種方法
一種是直接根據定義 b : a * b ≡ 1 (mod m)用擴歐算出來。
或者是用費馬小定理
a ^ (p - 1) ≡ 1 (mod p)
-> a ^ (p - 2) ≡ 1 / p (mod p)
當然用費馬小定理求的時候必須保證 p 是一個質數, 擴歐求得時候保證 a 和 p 互質就可以了。
3) 數論函數
在數論上,算術函數(或稱數論函數)指定義域為正整數、陪域為複數的函數,每個算術函數都可視為複數的序列。(嗯我並不知道這句話是什麼意思,,)
莫比烏斯函數
μ 的積性是很顯然的
有定理:
Σ(d|n) μ(d) = [n = 1]
不知道有什麼用
這個定理可以推出這個式子 : μ * {1} = e
莫比烏斯反演公式
以上分別是莫比烏斯反演的兩種形態。
證明
數論函數 e(n) = [n = 1]
-> f * e = f , μ * {1} = e
->f(n) = Σ(d|n) g(d) 即 f = g * {1}
g(n) = Σ(d|n) μ(d) * f(n / d) 即 g = f * μ
-> f * μ = g * {1} * μ = g * e = g
g * {1} = f * μ * {1} = f * e = f
得證
莫比烏斯函數與容斥原理
設 f(n) = ∑(d|n) φ(d), 由下面歐拉函數中的定理, f(n) = n
又 φ(n) = ∑(d|n) μ(d) * f(n / d), 即 φ(n) = ∑(d|n) μ(d) * n / d
通過例子感性地解釋莫比烏斯反演:
考慮不超過n的正整數k,根據歐拉函數的定義,我
們要算出有多少k與n互質。
令gcd(n, k) =??,當??>1要被去除,考慮質數集合
??={2,3,5,7,11,13,…},??>1時顯然會是??中某些
質數的倍數且 p 中不會含有平方因子。若??|??,可知滿足這樣條件的k有n/??個。
這些是需要去掉的,但很容易發現中間有重複。
對於有兩個質因子的p, 要加上 n/ p, 有三個的要再減去 n / p …… 有 t 個的要加上 (-1) ^ t * (n / p)
發現這其實就是莫比烏斯反演推出來的那個式子!
真是非常優美的。
歐拉函數
歐拉定理
對於互質的正整數 a 和 n ,有 aφ(n) ≡ 1 mod n 。
證明:
消去律:如果 gcd(c,p) = 1 ,則 ac ≡ bc mod p ⇒ a ≡ b mod p 。
( 1 ) 令 Zn = {x1, x2, ..., xφ(n)} , S = {a * x1 mod n, a * x2 mod n, ... , a * xφ(n) mod n} ,
則 Zn = S 。
① 因為 a 與 n 互質, xi (1 ≤ i ≤ φ(n)) 與 n 互質, 所以 a * xi 與 n 互質,所以 a * xi mod n ∈ Zn 。
② 若 i ≠ j , 那麼 xi ≠ xj,且由 a, n互質可得 a * xi mod n ≠ a * xj mod n (消去律)。
( 2 ) aφ(n) * x1 * x2 *... * xφ(n) mod n
≡ (a * x1) * (a * x2) * ... * (a * xφ(n)) mod n
≡ (a * x1 mod n) * (a * x2 mod n) * ... * (a * xφ(n) mod n) mod n
≡ x1 * x2 * ... * xφ(n) mod n
對比等式的左右兩端,因為 xi (1 ≤ i ≤ φ(n)) 與 n 互質,所以 aφ(n) ≡ 1 mod n (消去律)。
o(≧v≦)o~~好棒
費馬小定理即歐拉定理 n 為質數時的一個特殊情況。
兩個定理
∑(d|n) φ(d) = n
當n>1時, 1…n中與n互質的整數和為 n * φ(n) / 2
歐拉函數的積性
n = a * b, gcd(a, b) = 1, 則:
φ(n) = φ(a) * φ(b)
證明:
對於≤n 的每一個正整數, 都可以表示成 u = a * q + r (0 <= q < b, 1 <= r <= a)
如果要證明 gcd(u , n) = 1, 等價於證明 gcd(u, a) = 1 且 gcd(u, b) = 1
考慮保證 gcd(u, a) = 1, 即對於每一個確定可取的 q, 這裡的 r 都與a 不互質, 若且唯若u 與 a不互質
-> 對於每一個確定可取的 q, r都有 φ(a) 種取法。
下面考慮保證 gcd(u, b) = 1, 這是我們將 r 的取值確定, 對於 u1 = a * q1 + r, u2 = a * q2 + r, 如果 q1 != q2, 有u1 != u2 (mod n)。
反證 若 u1 = u2(mod n) -> a * q1 = a * q2(mod n) -> q1 = q2 (mod n) (消去率), 矛盾。
所以Sn = {u1, u2 …… ub} = {1, 2, …… b}, 故ui 中與b 互質的數有 φ(b) 個。
-> 對於每一個確定可取的 r, q都有 φ(b) 中取法。
綜上, φ(n) = φ(a) * φ(b)
歐拉函數通式
(1) p^k 的歐拉函數為 p^k - p^(k - 1)
這個很顯然, 在 p^k 個數中 p 的倍數一共有 p^(k - 1) 個
(2) 若 gcd(p, q) = 1, 則 φ(p * q) = φ(p) * φ(q)
積性函數的性質, 剛才已經證過了。
(3)結合前兩條, 顯然可以得出任意整數的歐拉函數的公式:
若 n = ∏ pi^ki, 則φ(n) = ∏ (pi^ki - pi^(ki - 1)) = ∏ pi^ki * (1 - 1/pi) = n * ∏(1 - 1/pi)
4)原根
階
設(a, m) = 1, 則滿足 a^r = 1 (mod m) 的最小正整數 r 叫做 a 模 m 的階。
有比較顯然的定理 : 若 r 為 a 模 m 的階, 且a^k = 1 (mod m), 若且唯若 r | k。
又有: r 為 a 模 m 的階 若且唯若以下兩條件成立:
1, a^r = 1(mod m)
2, 對於每個r 的質因子 p, 有 a^(r/p) != 1(mod m)
這個也是很顯然的吧,,
原根
若整數a模m的階爲φ(m),則a叫做是模m的原根
定理:對於正整數m,模m具有原根若且唯若m= 2;4;pa;2pa,
其中p是奇素數且a >= 1。
因為原根通常很小 , 一般用枚舉來找原根。
奇質數的所有原根
定理:如果p為素數,那麼素數p一定存在原根,並且p的原根的個數為phi(p-1).
設m是正整數,a是整數,若a模m的階等於φ(m),則稱a為模m的一個原根.
假設一個數g對於P來說是原根,那麼g^i mod P的結果兩兩不同,且有 1<g<P, 0<i<P,那麼g可以稱為是P的一個原根,歸根到底就是g^(P-1) = 1 (mod P)當且
僅當指數為P-1的時候成立.(這裡P是素數).
求原根目前的做法只能是從2開始枚舉,然後暴力判斷g^(P-1) = 1 (mod P)是否當且當指數為P-1的時候成立。而由於原根一般都不大,所以可以暴力得到.
求一個奇素數的所有原根方法。
設g是P的平方非剩餘,是P-1的標準分解式,若恒有成立,
則g就是P的原根。
5) 組合數求模
n * m 可以接受的話就直接 nm DP。
如果 m 比較小(m * logP 可以接受的話)直接把分子和分母都列出來算就好了(分子分母的數量都是 O(m)級的)對於分子和分母上的每一個數把它是P的素因子的因子都單獨提出來求, 其它的直接乘到一起分母再乘一個逆元就可以了。
n, m都非常大且P為質數的時候就可以用Lucas定理了
就是把n, m都轉化為 P 進位然後每一位單獨算就好了, 很簡單實用。
下面的問題是如果n, m都很大但P是合數怎麼辦T T。 顯然如果這個合數的每個質因子的質數都<=1的話直接CRT合并一下就好了, 所以下面問題在於如何求 C(n, m) % (P ^ c)
參考資料
數論pdf by 王迪
賈志鵬線性篩
JZPKIL解題報告 by gyz
孫子定理 百度百科
數論的歐拉定理證明 csdn 蓬蒿人
常用數論知識整理