初等數論中的歐拉(Euler)公式


這些基礎知識都是數論中基本,而在密碼學中數論又是基礎;

 數論基礎(質數篩法、同余、快速冪、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;

 
======================= **應用場景** =======================


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM