Math

Number Theory

INDEX

Prime

Greatest Common Divisor

Block

Euler's Totient Funciton

Prime

質數

1. Definition

2. Primality test

3. Sieve of Eratosthenes

Prime

Definition

定義

設整數\ p \neq 0, \pm 1。如果\ p\ 除了\ 1\ 之外沒有其他因數,p\ 為質數
\bf{如果沒有特別說明,質數是指正的質數}
簡單性質:
如果\ p\ 有大於\ 1\ 的因數\ d,則\ d=p
大於\ 1\ 的整數一定可以表示為多個質數的乘積
質數有無限多個
所有大於\ 3\ 的質數都可以表示為\ 6n\pm 1\ 的形式
(但滿足\ 6n\pm 1\ 的不一定為質數)

Prime

Primality Tests

怎麼判斷一個數是不是質數

確定性測試
可以百分百確定是不是質數
概率性測試
通常比確定性測試快,但有可能出錯
如\ \text{Miller-Rabin}\ 測試
(這會用到費馬小定理,今天先不講)

Prime

試除法

枚舉看是否能整除

bool isPrime(int a) {
	if (a < 2) return false;
    for (int i = 2; i < a; i++) {
    	if (a % i == 0) return false;
    }
    return true;
}
但可以發現如果\ x\ 是\ a\ 的因數,則\ \frac{a}{x}\ 也是\ a\ 的因數
\Rightarrow 對於每一對\ (x, \frac{a}{x})\ 我們只需要檢驗其中一個就好
bool isPrime(int a) {
	if (a < 2) return false;
    for (int i = 2; i * i < a; i++) {
    	if (a % i == 0) return false;
    }
    return true;
}
\text{Time Complexity}:O(n) \rightarrow O(\sqrt{n})

Prime

Sieve of Eratothenes

對於每一個質數\ p,我們可以知道\ p, 2p, 3p, ... ,kp\ 不為質數
vector<bool> is_prime(n + 1, true);
is_prime[0] = is_prime[1] = false;
for (int i = 2; i * i <= n; i++) {
    if (is_prime[i]) {
        for (int j = i * i; j <= n; j += i) {
            is_prime[j] = false;
        }
    }
}
\text{Time Complexity}:O(n \log \log n)
在\ O(n \log \log n)\ 的時間內找\ [1;n]\ 區間內的所有質數

GCD

最大公因數

1. 歐基里德算法

2. LCM

3. 擴展歐幾里德算法

GCD

歐基里德算法

假設\ a > b
設\ a=bk+c, c=a\bmod b,設\ d \mid a, d\mid b,則\ c = a-bk,\ \frac{c}{d} = \frac{a}{d} - \frac{b}{d}k
\frac{c}{d}\ 為整數,即\ d \mid c,所以對於\ a, b\ 的公因數,他也會是\ b,\ a\bmod b\ 的公因數
設\ d\mid b, d\mid (a\bmod b),得到\ \frac{a \bmod b}{d} = \frac{a}{d} - \frac{b}{d}k,\ \frac{a \bmod b}{d} + \frac{b}{d}k=\frac{a}{d}
\frac{a}{d}\ 為整數,即\ d\mid a,所以\ b,\ a\bmod b\ 的公因數也是\ a, b\ 的公因數
因為兩者公因數相同,所以最大公因數也相同
\Rightarrow gcd(a,b) = gcd(b,a\bmod b)

GCD

Implement

// Version 1
int gcd(int a, int b) {
	if (b == 0) return a;
    return gcd(b, a % b);
}

// Version 2
int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a % b);
}
迭代式:
int gcd(int a, int b) {
	while (b != 0) {
    	int tmp = a;
        a = b;
        b = tmp % b;
    }
    return a;
}
\text{Time Complexity}:O(\log max(a, b))
(\text{btw}在找費波那契數列相鄰兩項會達到最壞複雜度)

GCD

Least Common Multiple (LCM)

最大公倍數
設\ a=p^{k_{a_1}}_{1}p^{k_{a_2}}_{2}\cdots p^{k_{a_s}}_{s},b=p^{k_{b_1}}_{1}p^{k_{b_2}}_{2}\cdots p^{k_{b_s}}_{s}
\text{GCD}=p^{min(k_{a_1}, k_{b_1})}_{1}p^{min(k_{a_2}, k_{b_2})}_{2}\cdots p^{min(k_{a_s}, k_{b_s})}_{s}
因為\ k_a +k_b=max(k_a,k_b) + min(k_a,k_b)
\Rightarrow gcd(a,b) \times lcm(a,b) = a \times b
要求最小公倍數只需要先求最大公因數就好
\text{LCM}=p^{max(k_{a_1}, k_{b_1})}_{1}p^{max(k_{a_2}, k_{b_2})}_{2}\cdots p^{max(k_{a_s}, k_{b_s})}_{s}
其實\ \text{C++17}\ 有內建的
#include <numeric> // 在這裡面
// ...
std::gcd(a, b);
std::lcm(a, b);

GCD

擴展歐基里德算法

求\ ax + by = gcd(a,b)\ 的解
設\ ax_1 + by_1 = gcd(a, b),\ bx_2 + (a\bmod b)y_2 = gcd(b,a\bmod b)
由歐基里德定理可知:gcd(a, b) = gcd(b, a\bmod b)
又因為\ a\bmod b = a - (\lfloor \frac{a}{b}\rfloor \times b)
\Rightarrow ax_1 + by_1 = bx_2 + (a\bmod b) y2
ax_1 + by_1 = ay_2 + bx_2 - \lfloor \frac{a}{b} \rfloor \times by_2 = ay_2 + b(x_2 - \lfloor \frac{a}{b} \rfloor y_2)
\Rightarrow ax_1 + by_1 = bx_2 + (a - (\lfloor \frac{a}{b}\rfloor \times b))y2
將\ x_2,y_2\ 代入遞迴求解至\ gcd\ 為\ 0\ 時\ x=1,y=0\ 回去求解

GCD

Implement

int exgcd(int a, int b, int &x, int &y) {
	if (b == 0) {
    	x = 1;
        y = 0;
        return a;
    }
    int d = exgcd(b, a % b, x, y);
    int t = x;
    x = y;
    y = t - (a / b) * y;
    return d;
}
返回的值是\ gcd,在這個過程中會計算\ x, y
雖然\ ax + by =gcd(a, b)\ 的解有無限多個,但若\ b\neq 0 \\ 擴展歐基里德求出的解必有\ |x| \leq b, |y|\leq a

Block

數論分塊(應該沒特定英文名)

Block

分塊

如果要算 \sum^{n}_{i = 1}\lfloor \frac{n}{i} \rfloor
你可以\ O(n)\\ 但如果\ n\ 太大(10^9\ 之類)會爛
發現對於固定的\ n,\lfloor n/i\rfloor 在某些區間上值相同
所以當\ \lfloor n/i \rfloor\ 等於某個固定值\ v\ 時,\ i\ 會落在一個連續區間裡
假設\ \lfloor n/i \rfloor = v,可以推出
v=\lfloor \frac{n}{i}\rfloor \iff v \leq \frac{n}{i} < v + 1 \iff \frac{n}{v + 1} < i \leq \frac{n}{v}
\Rightarrow 將求和區間\ 1 \leq i \leq n\ 拆成若干個區間\\就可以一次性把整個區段貢獻算進去

Block

Implement

long long sum(long long n) {
	long long ret = 0, i = 0;
    while (i <= n) {
    	long long val = n / i; // floor(n / i)
        long long r = n / val; // 讓 floor(n / k) = val 的區間右界
       	if (r > n) r = n;
        ret += val * (r - i + 1);
        i = r + 1;
    }
    return ret;
}
\text{Time Complexity}:O(\sqrt n)
分塊是個概念這裡講的是數論分塊最簡單的而已

Euler's totient function

歐拉函數

1. Property

2. Divisor Sum Property

Euler's totient function

性質

歐拉函式\ \phi(n)\ 代表\ 1\ 到\ n-1\ 中與\ n\ 互質的數的個數
如果\ p\ 是質數\rightarrow \phi(p) = p - 1
如果\ p\ 是質數且\ k \geq 1,則\ 1\ 到\ p^k\ 之間有剛好\ p^k/p\ 個數字被\ p\ 整除 \\\rightarrow \phi(p^k) = p^k - p^{k-1}
根據中國剩餘定理,對於任意\ 0\leq x < a\ 和\ 0 \leq y < b,存在唯一的\ z,使得:
z \equiv x \pmod a,\quad z \equiv y \pmod b,\quad 且\ 0 \leq z < ab
一個數\ z\ 與\ ab\ 互質,若且唯若\ z\ 與\ a\ 互質,且\ z\ 與\ b\ 互質
將\ z\ 分解為\ (x,y) \rightarrow x\ 必須與\ a\ 互質且\ y\ 也必須與\ b\ 互質
\phi(ab) = \phi(a) \cdot \phi(b)
\text{Proof}
\Rightarrow 與\ ab\ 互質的數\ z\ 的數量等於所有\ (x, y)\ 的組合數量\ \phi(a)\cdot \phi(b)

Euler's totient function

性質

用剛剛三個性質:
\phi(n) = \phi(p_1^{a_1}) \cdot \phi(p_2^{a_2}) \cdots \phi(p_k^{a_k})
=(p_1^{a_1} - p_1^{a_1 - 1}) \cdot (p_2^{a_2} - p_2^{a_2 - 1}) \cdots (p_k^{a_k} - p_k^{a_k - 1})
=p_1^{a_1} \cdot (1-\frac{1}{p_1}) \cdot p_2^{a_2} \cdot (1-\frac{1}{p_2}) \cdots p_k^{a_k} \cdot (1-\frac{1}{p_k})
=n \cdot (1-\frac{1}{p_1}) \cdot (1-\frac{1}{p_2}) \cdots (1-\frac{1}{p_k})
=n \prod_{p\mid n} (1-\frac{1}{p})
再把剛剛那個推廣
\phi(ab) =ab \prod_{p\mid ab} (1-\frac{1}{p})
=ab \frac{\prod_{p\mid a}(1 - \frac{1}{p}) \prod_{p\mid b}(1-\frac{1}{p})}{\prod_{p\mid gcd(a,b)}(1-\frac{1}{p})} \cdot \frac{gcd(a,b)}{gcd(a,b)}
=\phi(a)\phi(b)\frac{gcd(a,b)}{\phi(gcd(a,b))}

Euler's totient function

Implement

利用\ \phi(n)=n \prod_{p\mid n} (1-\frac{1}{p})\ 可以計算出\ \phi(n)
int phi(int n) {
	int result = n;
    for (int i = 2; i * i <= n; i++) {
    	if (n % i == 0) {
        	while (n % i == 0) {
            	n /= i;
            }
            result -= result / i;
        }
    }
    if (n > 1) { // n is prime
    	return n - 1;
    }
    return result;
}
\text{Time Complexity}:O(\sqrt n)

Euler's totient function

Implement

也可以利用跟質數\ \text{Sieve of Eratosthenes}\ 類似的算法計算\ [1;n]\ 的所有\ \phi\ 值
void phi_1_to_n(int n) {
    vector<int> phi(n + 1);
    for (int i = 0; i <= n; i++)
        phi[i] = i;

    for (int i = 2; i <= n; i++) {
        if (phi[i] == i) {
        	// i is prime
            for (int j = i; j <= n; j += i) {
                phi[j] -= phi[j] / i;
            }
        }
    }
}
\text{Time Complexity}:O(n \log \log n)

Euler's totient function

Divisor Sum Property

\sum_{d\mid n} \phi(d)=n
令\ S\ 代表集合\ \{1,2,...,n\}
對於\ n\ 的每一個因數\ d:
S_d=\{a \mid 1 \leq a \leq n, \; gcd(a, n)=d\}
令\ a=d \cdot k \Rightarrow gcd(dk, n) = d \Rightarrow gcd(k, \frac{n}{d}) = 1
\text{Proof}
對於每一個\ a,gcd(a, n)\ 只有唯一一個\ d
所以\ k\ 與\ \frac{n}{d}\ 互質,同時\ 1 \leq dk \leq n \Rightarrow 1 \leq k \leq \frac{n}{d}
k\ 的數量為\ \phi(\frac{n}{d})
所以得到\ \sum_{d\mid n}|S_d|=\sum_{d\mid n}\phi(\frac{n}{d})=\sum_{d\mid n}\phi(d)=n

Euler's totient function

Euler's Theorem

所以你也可以計算\ [1;n]\ 的\ \phi
void phi_1_to_n(int n) {
    vector<int> phi(n + 1);
    phi[0] = 0;
    phi[1] = 1;
    for (int i = 2; i <= n; i++) {
        phi[i] = i - 1;
    }

    for (int i = 2; i <= n; i++) {
        for (int j = 2 * i; j <= n; j += i) {
        	phi[j] -= phi[i];
        }
    }
}
\text{Time Complexity}:O(n \log n)

題單

掰掰

Math

By keaucucal

Math

  • 308