這些基礎知識都是數論中基本,而在密碼學中數論又是基礎;
數論基礎(質數篩法、同余、快速冪、gcd、裴蜀定理)
======================= **基礎知識** =======================
歐幾里得算法:
gcd(a, b) : 求a, b 最大公約數: 輾轉相除法: 通過講一個較大規模問題換換成為一個較小問題;(leetcode 1071 很有意思的題目,看能不能在思維模式中轉換成輾轉相除方法);
證明:
1) a, b 的最大公約數也是 b, a % b 的公約數:
假設a, b 的最大公約數是 g, 則:
a = k1 * g;
b = k2 * g;
a % b = a - k3 * b = k1 * g - k2 * g * k3 = (k1 - k2 * k3) g; (a % b 也可以理解為 a - k3 * b 后剩余一個小於b 的值);
2) a, b的最大公約數也是 b, a % b 的最大公約數;
反證法: 假設b, a % b 最大公約數為 d, 則gcd(k2, k1 - k2 * k3) = d
k2 = m * d; k1 - k2 * k3 = n * d;
a = k1 * g = (n * d + m * d * k3) * g= (n + m * k3) * d * g;
b = k2 * g = m * d * g;
如果 d != 1 的話, g 就不會是a , b 最大公約數(d * g), 與假設條件g 是 a, b 最大公約數相悖, 所以 d = 1;
code :

1 int gcd(int a, int b) { 2 if(!b) return a; 3 cout << "gcd(" << a << ", " << b << ") = " ; 4 return gcd(b, a % b); 5 } 6 7 int main() 8 { 9 int a,b; 10 while(cin >> a >> b) cout << gcd(a, b) << endl; 11 return 0; 12 }
擴展歐幾里得算法:
貝祖等式(裴蜀等式):
a * x + b * y = gcd(a, b) = c; ====> 一定有解
b * x0 + (a % b) * y0 = c;
b * x0 + (a - k * b) * y0 = c;
a * y0 + (x0 - k * y0) * b = c;
x = y0 , y = x0 - k * y0 = x0 - (a / b) * y0; 這樣就可以根據下一級的解,求得上一層的解;
code:

int ex_gcd(int a, int b, int &x, int &y){ if(!b) { x = 1, y = 0; cout << a << " * " << x << " + " << b << " * " << y << " = "; return a; } int ret = ex_gcd(b, a % b, y, x); y -= a / b * x; cout << a << " * " << x << " + " << b << " * " << y << " = "; return ret; } int main() { int a, b, x, y, ret = 0; while(cin >> a >> b) { ret = ex_gcd(a, b, x, y); cout << ret << endl; } return 0; }
有了上面的貝祖等式,那么對於解決 a * x mod b = c 的問題,就可以變成 a * x - k * b = c == > a * x + b * y = c (其中c 可以為最大公約數的倍數);
而對於這個式子,也就某種程度上的Euler 公式;
下面內容有參考(數論的歐拉證明:歐拉公式):
歐拉公式: a φ(n) % n ≡ 1; 其中 a 與 n 互質的正整數;
≡ :數論中表示同余;
歐拉函數φ(n): 對於一個正整數,為小於n 且與n 互質的正整數(包含1) 的個數;
定義小於 n 且與 n 互質的數構成的集合為Zn, 稱呼這個集合為n 的完全余數集合, |Zn| = φ(n)
1). 對於 素數 n : φ(n) = n - 1; 反之,如果一個數滿足 φ(n) = n - 1, 則n 是素數;
2). 如果n 是素數, a是一個正整數,那么φ(na) = na - na-1;
證明: 因為n 為素數,則na中只有一個素因子n, 其中與n 不互質的個數就是n的倍數個數 na-1 - 1;
3). 如果 n = p * q (p, q 互質),則φ(n) = φ(p) * φ(q) = (p - 1) * (q - 1);
證明如下:Zn = {1, 2, 3, ... , n - 1} - {p, 2p, ... , (q - 1) * p} - {q, 2q, ... , (p - 1) * q} ==>
φ(n) = (n - 1) - (q - 1) - (p - 1) = (p -1) * (q -1) = p * q - p - q + 1 = (p - 1) * (q - 1) = φ(p) * φ(q) 。
4). 如果 n = pk, φ(n) = pk - pk - 1 ===> n = pk * qm , φ(n) = pk-1 * (p - 1) * qm-1 * (m - 1); //p, q 都是素數, gcd(p, q) = 1;
證明: 小於pk 的正整數個數為pk - 1 - 1個,其中和 pk 不互質的正整數有{p * 1, p * 2, p * 3, ... , p * (pk-1 - 1)}, 所以 φ(n) = pk - pk - 1
5). 設n=p1a1p2a2…pkak 為正整數n的素數冪分解,那么φ(n)=n(1-1/p1)(1-1/p2)…(1-1/pk)(算法的核心)
證明: 由上面2, 3 可推導:φ(n) = φ(p1a1) * φ(p2a2) ... *φ(pkak) = (p1a1 - p1a1-1) ... (pkak - pkak-1) = n(1-1/p1)(1-1/p2)…(1-1/pk)
6). 如果n大於2,那么n的歐拉函數值是偶數。

int phi(int n) { int ans = n, x = 2; while(x * x <= n){ if(n % x == 0) ans -= ans / x; //這里就是根據上面5 公式得到 while(n % x == 0) n /= x; //不斷將n 中將 x 去除,也就是去除pk ^ ak; x += 1; } if(n != 1) ans -= ans / n; return ans; } int main() { int n; while(cin >> n) cout << "phi(" << n << ") = " << phi(n) << endl; return 0; }
同余的幾個性質
性質1:a≡a(mod m),(自反性)
性質2:若a≡b(mod m),那么b≡a(mod m)(對稱性)
性質3:若a≡b(mod m),b≡c(mod m)=>a≡c(mod m)(傳遞性)
性質4:若a≡b(mod m),c≡d(mod m),那么a±c≡b±d(mod m)(可加減性)
證明:設a=A+Ka*m,b=A+Kb*m,c=C+Kc*m,d=C+Kd*m則(a±c)%m=(A±C),(b±d)%m=(A±C)即a±c≡b±d(mod m)
性質5:若a≡b(mod m),c≡d(mod m),那么ac≡bd(mod m)(可乘性)
證明:設a=A+Ka*m,b=A+Kb*m,c=C+Kc*m,d=C+Kd*m則ac=( A+Ka*m)( C+Kc*m),bd=( A+Kb*m)( C+Kd*m)所以ac%m=AC bd%m=AC即ac≡bd(mod m)
性質6:若a≡b(mod m),那么an≡bn(mod m)(其中n為自然數)
證明:由性質1和性質5得。
性質7:若ac≡bc(mod m),(c,m)=1,那么a≡b(mod m)
證明:ac≡bc(mod m)=>c(a-b)≡0(mod m)=>c%m*(a-b)%m=0 =>m|c或m|(a-b)又因為(m,c)=1.所以m|(a-b)即a≡b(mod m)
性質8:若a≡b(mod m),那么a^t≡b^t(mod m)
證明:由性質5得。
性質9:若 a≡b(mod m1) a≡b(mod m2)…. a≡b(mod mk) 則 a≡b(mod [m1,m2……mk])
證明:由題意得mi|(a-b) (1<=i<=k)即(a-b)是mi的公倍數,所以[m1,m2……mk]|(a-b)即a≡b(mod [m1,m2……mk])
歐拉定理 :
對於互質的正整數 a 和 n ,有 aφ(n) ≡ 1 mod n 。
在應用中, a0 ≡ 1 (mod n), a1 ≡ r1 (mod n), a2 ≡ r2(mod n) ... a φ(n) ≡ 1(mod n), a φ(n) + 1 ≡ r1(mod n), 這里存在循環節,循環節的長度最長為φ(n)。
證明:
( 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 (消去律)。
注:
消去律:如果 gcd(x,n) = 1 ,則 ax ≡ bx mod n ⇒ a ≡ b mod n 。
a * x % b = gcd(a, b) = c ==> a * x - k * b = c; ==> a * x + b * y = c;
======================= **代碼演示** =======================
1. Euler 素數篩(時間復雜度 O(n))

1 #include <iostream> 2 using namespace std; 3 #define MAX_N 10000 4 int prime[MAX_N + 5]; //1:非質數; 0: 質數 5 6 void initPirme(){ 7 //prime[0] 記錄prime 個數,后面依次記錄各個質數 8 for(int i = 2; i <= MAX_N; ++i) { 9 if(!prime[i]) prime[++prime[0]] = i; 10 11 for(int j = 1; j <= prime[0]; ++j) { 12 if(i * prime[j] > MAX_N) break; 13 //下2步是歐拉質數篩核心:每一個合數必定為 (最大因子 * 最小質數), 14 //所以當找到最小質數時(i % prime[j] = 0), 意味着對於依靠i 與其他質數 生成的合數一定可以通過prime[i] 與其他更大的i生成 15 //這也是Euler 素數篩事件復雜度為O(n) 的原因 16 prime[i * prime[j]] = 1; 17 if(i % prime[j] == 0) break; 18 } 19 } 20 return; 21 } 22 23 24 int main() 25 { 26 initPirme(); 27 cout << prime[0] << endl; 28 for(int i = 1; i <= prime[0]; ++i) cout << prime[i] << " "; 29 cout << endl; 30 return 0; 31 }
======================= **經典問題** =======================
求解: ax mod b = c 中,已知a, b, c 三個正整數值,其中a, b 互質,求 x 的最小正整數解;
方法1: 根據上面的知識,可以得到當a, b 互質時,取余預算存在循環節,根據Euler函數求得最大循環節長度,然后枚舉這些值,直到找到x 值;
方法2: 分塊算法,如下
經過上面的轉化,就將問題轉換成了 X1 * a1 + b * y = c, 其中 a1 可能為 a0 ~ am - 1, 那么通過枚舉a1 可能的值,然后使用擴展歐幾里德算法求得X1 值。
而 ak * m 對應的值,就在分塊算法中某一塊里面,如果存在的話,那么 x = k * m + r;
======================= **應用場景** =======================