这些基础知识都是数论中基本,而在密码学中数论又是基础;
数论基础(质数筛法、同余、快速幂、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;
======================= **应用场景** =======================