这些基础知识都是数论中基本,而在密码学中数论又是基础;

 数论基础(质数筛法、同余、快速幂、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 }
gcd

 

扩展欧几里得算法: 

贝祖等式(裴蜀等式):

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;
}
ex_gcd

 

有了上面的贝祖等式,那么对于解决 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;
}
Euler function

 

同余的几个性质

性质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;

 
======================= **应用场景** =======================

posted on 2021-12-26 23:08  学海一扁舟  阅读(2248)  评论(0编辑  收藏  举报