Math

Math Theory Part 2

Author: Lemon

Speaker: Ian Wen

模運算

簡單來說就是除以\(N\)的餘數 (?

注意,模運算討論的範圍僅限整數 (畢竟這樣才會有餘數)

模運算

要注意的是

C++模數的結果有可能是負的

e.g.

(-10) % 3 = -1

 

反正牽涉模運算時處理都要小心一點

但其實是 -10 mod 3 是 2

模運算

自從去過校培後

我自己會把模運算寫成函式

避免出錯啦

int mabs(long long a, int mod) { //轉成 0 <= a < mod的形式
    return (a % mod + mod) % mod;
}
int madd(long long a, long long b, int mod) { // a + b
    return mabs(a % mod + b % mod, mod);
}
int mmin(long long a, long long b, int mod) { // a - b
    return mabs(a % mod - b % mod, mod);
}
int mmul(long long a, long long b, int mod) {
    return mabs((a % mod)*(b % mod), mod);
}

模除(?

模除(?

你會發現剛剛沒有除法

因為在模運算中我們不能直接使用除法

e.g.

\(2 \equiv 5\ (mod\ 3)\)

如果用除法的話 (兩邊同除 5 看看)

\(0.4 \equiv 1\ (mod\ 3)\)

顯然是爛ㄉ

模反元素

就是倒數、反矩陣的概念

假設 \(a \equiv k\ (mod\ p)\)

它的模反元素\(a^{-1}\)使得

\(a \times a^{-1} \equiv k \times a^{-1} \equiv 1\ (mod\ p)\)

啊要怎麼算出來ㄋ(?

(aka 模逆元 乘法反元素)

注意,a, p 互質才有模逆元 !

e.g \(2 \times a^{-1} \equiv 1\ (mod\ 4)\) 你求不出模逆元

費馬小定理

\textit{Let}\ a \in \mathbb{Z},\ p\ \textit{is a prime}, \\ a ^ {p} \equiv a\ (mod\ p)

啊我就不證明ㄌw

我們可以從此推得

\(a \times a^{p-2} \equiv 1\ (mod\ p)\)

 

\(a^{p-2} \equiv a^{-1}\ (mod\ p)\)

a^{-1}

兩邊同乘

費馬小定理

於是

我們套分治學的快速冪

便能在\(O(logn)\)下求出模反元素

#include <iostream>
using namespace std;
const int MOD = 1e9 + 7; //常見的 10^9 + 7 就是個質數
int mabs(long long a, int mod = MOD) {
    return (a % mod + mod) % mod;
}
int madd(long long a, long long b, int mod = MOD) { // a + b
    return mabs(a % mod + b % mod, mod);
}
int mmin(long long a, long long b, int mod = MOD) { // a - b
    return mabs(a % mod - b % mod, mod);
}
int mmul(long long a, long long b, int mod = MOD) {
    return mabs((a % mod)*(b % mod), mod);
}
int power(int a, int b) { //快速冪
    int ret = 1;
    while(b) {
        if(b & 1) ret = mmul(ret, a);
        b >>= 1;
        a = mmul(a, a);
    }
    return ret;
}
int main() {
    int a;
    cin >> a;
    cout << mmul(a, power(a, MOD-2)) << '\n'; //這項恆為1
    cout << "INV:" << power(a, MOD-2) << '\n';
    return 0; 
}

歐拉定理

若 \(a, n \in \mathbb{Z},\ gcd(a, n) = 1\)

則 \(a^{\phi(n)} \equiv 1\ (mod\ n)\)

其中 \(\phi(n)\) 稱為歐拉函數

其值為小於 \(n\) 且與 \(n\) 互質的正整數個數

顯而易見的

若 p 是質數,則 \(\phi(p) = p - 1\)

等同於費馬小定理

耶你學會了第2種推導模逆元的方法

如果模的數不是質數怎麼辦

我們當然有更加通用的方法

但是比較難寫

沒關係

你們還是要會ouo

放棄(X

貝祖定理

ax + by = gcd(a, b)

有整數解

大概就這樣(?

當 \(a, b \in \mathbb{Z}\)

擴展歐幾里得算法

這才是重點w

我們可以透過擴展歐幾里得算法計算貝祖定理的解

ㄛ如果你不知道的話 歐幾里得算法就是輾轉相除法

int gcd(int a, int b) {
    if(a > b) return gcd(b, a);
    if(!b) return a;
    return gcd(b, a%b);
}

一般的輾轉相除法

擴展歐幾里得算法

\begin{cases} ax_1 + by_1 = gcd(a, b) \\ bx_0 + (a\%b)y_0 = gcd(a, b) \end{cases} \\ \text{又}a\%b = a - b \times \lfloor \frac{a}{b} \rfloor \\ \text{解聯立得} \begin{cases} x_1 = y_0 \\ y_1 = x_0 - \lfloor \frac{a}{b} \rfloor y_0 \end{cases}

我們知道在程式運行時當\(b = 0\)時\(gcd(a, b) = a\)

故遞迴結束時 我們會知道\(x = 1, y = 0\)

反覆往上推就能找到答案ㄌ

擴展歐幾里得算法

#include <iostream>
using namespace std;
int exgcd(int a, int b, int& x, int& y) { //加參考才能由下而上改變x, y
    if(a < b) return exgcd(b, a, y, x);
    if(!b) {
        x = 1;
        y = 0;
        return a;
    }
    int ret = exgcd(b, a%b, x, y);
    int temp = x; //暫存x0
    x = y;
    y = temp - a / b * y;
    return ret;
}
int main() {
    int a, b, x, y;
    cin >> a >> b;
    cout << exgcd(a, b, x, y) << '\n';
    cout << a * x + b * y << '\n'; //貝祖定理
}

我們便透過擴展歐幾里得算法求得貝祖定理ㄉ一個整數解

貝祖定理

知道 x、y 有什麼用(?

若 \(gcd(a, b) = 1\),也就是 a, b 互質

ax + by = gcd(a, b) = 1
ax \equiv 1\ (mod\ b)

x \equiv a^{-1}\ (mod\ b)

就求出模反元素ㄌ

複雜度同歐幾里得算法:\(O(log n)\)

要證明很難

例題

特別感謝一二學術長 lemonilemon 讓我偷簡報

數論 Part 2

By wen Ian

數論 Part 2

  • 162