數論基礎(更新中)


數論基礎(更新中)

標簽: 算法筆記 數論



一、入門知識

本單元難度\(\le\)小學六年級數學。

1.整數除法

除法是四則運算運算之一,作為乘法的逆運算。已知積與其中一個因數求另一因數的運算叫做除法.。

整數除法常有如下表達:

\[a \div b = c \cdots d \]

一般地,我們稱 a 為被除數,b 為除數,c 為商,d 為余數.

亦可簡單推出如下逆運算:

\[b \times c + d = a \]

2.整除

如果 a 能把 b 除盡,也就是\(a \div b\)余數為0,則我們稱 a 整除 b ,也稱 ba 整除.
記為:

\[a|b \]

中間的豎杠表示為整除符號,讀作:a 整除 b.

數論之路,皆由“整除”始。

3.整除的性質

  • 自反性
    對於任意 \(n\),有\(n|n\).

  • 傳遞性
    對於任意 \(a|b,b|c\),都有\(a|c\).

  • 反對稱性
    對於任意 \(a | b, b | a\), 都有\(a = b\).

4.約數與倍數

如果\(a|b\),那么稱 ab 的約數,ba 的倍數。同時稱,ab 的因子(因數)。

因此,我們有一個重要推論:

對於任何整數\(n \ge 2\)\(n\)至少有兩個因子:1和 \(n\)(它本身).
我們將這兩個因子稱為\(n\)平凡因子.

quiz1.如何計算\([1, n]\)中每個數因數的個數?

int p_num[MAXN];

for(int i = 1; i <= N; i ++)
    for(int j = i; j <= N; j += i)
        p_num[j] = 1;
    
//O(nlogn)

5.質數

一個整數不存在非平凡因子,我們就稱它為質數(亦稱為素數).
不是質數的整數我們稱它為合數,即合數有大於等於一個非平凡因子.
特殊地,1 不是質數,是合數.

例如:
2 只存在兩個平凡因子,即12,不存在非平凡因子.2 是質數.
5 只存在兩個平凡因子,即15,不存在非平凡因子.5 是質數.
4 存在非平凡因子 2. 4 不是質數,是合數.
1e9+7 不存在非平凡因子.1e9+7 是質數.

數論都是圍繞質數概念所展開,理解質數是走進數論大廈的第一步。

6.判斷質數

對於任意整數\(n\),判斷在\([2, \sqrt{n}]\) 是否存在整數\(i\),使得\(i | n\).
存在這樣的整數\(i\)\(n\)是合數.
否則,\(n\) 即為質數.

證明:
對於任何非平方數 \(n\),它的因子一定成對出現.
對於平方數 \(n\),除因子\(\sqrt{n}\)外,其余因子亦成對出現.
假設\(n\)存在一個因子 \(i\) 落在 \((\sqrt{n}, n)\) 區間,與 \(i\) 相對應的 \(n\) 的另一個因子 \(n / i\) 一定落在 \([2, \sqrt{n})\)這個區間。因為因子成對出現,所以只要判斷\([2, \sqrt{n}]\) 這個區間是否存在因子就可以。

bool is_prime(int n)
{
    if(n == 1)  return false;               //1是合數.
    for(int i = 2; i * i <= n; i ++)        //在[2, sqrt(n)]的區間中找因子.
        if(!(n % i))           
            return false;
            
    return true;                            //沒找到因子是質數.
}

//O(sqrt(n))

quiz2. 質數有無限個.

如何證明?

反證法:
假設質數的個數為有限個,分別為:\(p_1,p_2,p_3,\cdots,p_n\).

\(M = p_1 \times p_2 \times p_3 \times \cdots \times p_n + 1\).
易得,\(M \mod p_1 = 1, M \mod p_2 = 1, M \mod p_3 = 1, \cdots , M \mod p_n = 1\).
所以,\(M\)只存在\(1, M\)兩個因子,即\(M\)也是質數,但\(M \notin \{i \leq n | q_i \}\).與假設矛盾.
所以,假設結論的逆命題,質數的個數為無限個成立.

tip 關於質數分布的一點小性質

雖然我們目前沒有摸清楚質數的具體分布情況,但是我們能給出近似的:

\(\pi (n)\)為不超過\(n\) 的質數個數:$$\pi(n) \sim \frac{n}{\ln{n}}$$

由此可得推論:

  • \(n\,\) 質數附近的質數密度是\(1/\ln{n}\).
  • \(\,n\)個素數\(p_n \sim n\ln{n}\).

7.如何求質數?

  • 朴素法
    運用#6 如何判斷質數的方法,對區間每個數進行枚舉,然而這個方法實在算不上高效,復雜度是\(\sum_{i=1}^{n}\sqrt{i} = O(n\sqrt{n})\)
int isprime[MAXN], prime[MAXN], cnt = 0;

isprime[1] = 1;
for(int i = 2; i <= N; i ++)
    for(int j = 2; j * j <= i; j ++)
        if(!(i % j)){
            isprime[i] = 1;
            break;
        }
for(int i = 1; i < N; i ++)
    if(!isprime[i])
        prime[++ cnt] = i;
//O(n * sqrt(n))
  • 埃氏篩法

何為篩法?篩,就是篩選的意思。在一個區間中篩選出不是素數的數來,剩下的數就是素數.
首先找到第一個素數\(2\),用\(2\) 篩掉區間中所有\(2\) 的倍數(這些倍數因為已經有非平凡因子了,就一定是合數.),再用第二個素數\(3\) 篩掉區間中所有\(3\) 的倍數.當詢問到\(4\) 的時候,因為\(4\) 已經被\(2\) 篩掉了,所以\(4\) 不是質數.第三個質數是\(5\),以此類推.篩掉區間的所有合數.
埃氏篩法用到了篩法的思想,時間復雜度為\(O(n\ln{\ln{n}})\,\)時間復雜度雖然差於線性篩(一個合數可能被多個質數篩過),但也已經非常接近\(O(n)\)了.

int isprime[MAXN], prime[MAXN], cnt = 0;

isprime[1] = 1;
for(int i = 2; i <= N; i ++){
    if(!isprime[i]){                        //若是此數沒有被之前任何一個素數篩掉,那么它就是質數.
        prime[++ cnt] = i;
        for(int j = 2; i * j <= N; j ++)    //接着用這個質數,篩掉它的倍數.
            isprime[i * j] = 1;
    }
}

//>O(n)
  • 歐拉線性篩法

對於小范圍質數來說(算法競賽范圍),歐拉篩法是一種很實用的素數求法.還可以用這種篩法篩出歐拉函數.
歐拉篩的思路也是篩掉合數,留下的沒被篩掉的數就是質數.歐拉篩優於埃氏篩的地方在於歐拉篩保證每個數都只被其最小的質因數篩掉.因此每個數都只被篩掉一次,所以是線性的.

int isprime[MAXN], prime[MAXN], cnt = 0;

for(int i = 2; i <= N; i ++){
    if(!isprime[i])
        prime[++ cnt] = i;
    for(int j = 1; j <= cnt && i * prime[j] <= N; j ++){
        isprime[i * prime[j]] = 1;
        if(i % prime[j] == 0)    //這個if語句是關鍵.當i中包含了一個質因數時,說明再往后的乘積
            break;               //的最小質因數就都是此刻i中包括的那個了質因數了,而不是之后的
    }                          //prime[i]了,這就不滿足每個合數只能被它最小的質因數篩掉的性質了.
}

//O(n)

8.質因數分解

\(1\)以外的任何正整數都可以拆成質數乘積的方式,這個過程叫做質因數分解.
例如:
\(5 = 5 = 5^1\)
\(15 = 3 \times 5 = 3^1 \times 5^1\)
\(24 = 2 \times 2 \times 2 \times 3 = 2^3 \times 3\)
\(60 = 2 \times 2 \times 3 \times 5 = 2^2 \times 3 \times 5\)
基本算術定理規定,任何一個大於1的正整數,這樣的質因數分解形式是唯一的.

在數學中,對於小整數,我們可以使用短除法來簡單求出一個數的質因數的分解.
短除法

質因數分解可以在\(O(\sqrt{n})\)中完成.

int factorize(int x, int p[])        // 將x進行質因數分解,並將質因數存放在數組p中,返回質因數個數.
{
    int cnt = 0;                            //cnt記錄素因數個數.
    for(int i = 2; i * i <= x; i ++){      // 在[2, sqrt(x)]中枚舉約數.
        while(x % i == 0){                 //找到一個質因數就將其不斷地從x中除去.
            p[++ cnt] = i;
            x /= i;
        }
    }
    
    if(x > 1)  p[++ cnt] = i;             //如果最后所剩的數大於1,說明還是素數,要放在p中.
    
    return cnt;
}

quiz3.在質因數分解下,如何表示a * b, a / b, a | b?

設$$a = p_1^{a_1}p_2^{a_2}p_3^{a_3}\cdots, $$ $$b = p_1^{b_1}p_2^{b_2}p_3^{b_3}\cdots,$$ $$p_i \in { 小於等於\max({a, b})的全部質數,共n個. },0 \le a_i,b_i. $$
則有
$a \times b = p_1^{a_1 + b_1} \bullet p_2^{a_2 + b_2} \bullet p_3^{a_3 + b_3} \bullet \cdots \( \)a \div b = p_1^{a_1 - b_1} \bullet p_2^{a_2 - b_2} \bullet p_3^{a_3 - b_3} \bullet \cdots \( \)a | b = a_i \le b_i, \forall, i \le n$

quiz4.在質因數分解下,如何求一個數的約數個數?

設$$a = p_1^{a_1}p_2^{a_2}p_3^{a_3}\cdots,
p_i \in { 小於等於\max({a, b})的全部質數,共n個.},; 0
\le a_i.$$
則有\(d(n) = (1 + a_1) \bullet (1 + a_2) \bullet (1 + a_3) \bullet \cdots.\)

9.最大公約數與最小公倍數

最大公約數,英文為greatest common divisor,簡記為gcd.
最小公倍數,英文為least common multiple,簡記為lcm.

什么是最大公約數?什么是最小公倍數?

\(gcd(a, b)\)代表\(a\)\(b\)的最大公約數.令\(r(a) = \{d\;|\;a\,\,mod\,\,d = 0\}, r(b) = \{d\;|\;b\,\,mod\,\,d = 0\}\),則\(gcd(a, b) = max\{d \in [r(a) \cap r(b)]\}\). 即\(gcd(a,b)\)為能同時整除\(a,b\)的最大的整數.

\(lcm(a, b)\)代表\(a\)\(b\)的最大公倍數.令\(R(a) = \{m\;|\;m \,\,mod\,\, a = 0\}, R(b) = \{m\;|\;m \,\,mod\,\, b = 0\}\),則\(lcm(a, b) = min\{m \in [R(a) \cap R(b)]\}\). 即\(lcm(a,b)\)為能同時被\(a, b\)整除的最小整數.

例如,\(gcd(10,12) = 2,\; gcd(36, 48) = 12,\; gcd(25, 36) = 1,\;lcm(12, 20) = 120,\;lcm(7, 11) = 77\).

證明\(gcd(a, b) \times lcm(a, b) = a \times b\).

根據基本算術定理,我們可以將\(a\)表達為\(a = p_1^{a_1}p_2^{a_2}p_3^{a_3}\cdots\), 將\(b\)表達為\(b = a = p_1^{b_1}p_2^{b_2}p_3^{b_3}\cdots\).

因為\(gcd(a,b)\)是能同時整除\(a, b\)的最大整數,所以\(gcd(a, b) = p_1^{\min{(a_1, b_1)}}p_2^{\min{(a_2, b_2)}}p_3^{\min{(a_3, b_3)}}\cdots\).若\(p_i\)的指數小於\(\min{(a_i, b_i)}\),則不滿足最大,如\(p_i\)的指數大於\(\min{(a_i, b_i)}\),則不滿足能整除\(a, b\).
因為\(lcm(a, b)\)是能同時被\(a, b\)整除的最小整數,所以\(lcm(a, b) = p_1^{\max{(a_1, b_1)}}p_2^{\max{(a_2, b_2)}}p_3^{\max{(a_3, b_3)}}\cdots\),若\(p_i\)的指數大於\(\max{(a_i, b_i)}\),則不滿足最小,如\(p_i\)的指數小於\(\min{(a_i, b_i)}\),則不滿足同時能被\(a, b\)整除.

綜上,\(gcd(a, b) \times lcm(a, b) \\ = p_1^{\min{(a_1, b_1)}}p_2^{\min{(a_2, b_2)}}p_3^{\min{(a_3, b_3)}}\cdots \times p_1^{\max{(a_1, b_1)}}p_2^{\max{(a_2, b_2)}}p_3^{\max{(a_3, b_3)}}\cdots \\ = p_1^{\max{(a_1, b_1)}+\min{(a_1, b_1)}}p_2^{\max{(a_2, b_2)} + \min{(a_2, b_2)}}p_3^{\max{(a_3, b_3)}+\min{(a_3,b_3)}}\cdots\\ = p_1^{(a_1 + b_1)}p_2^{(a_2 + b_2)}p_3^{(a_3+b_3)}\cdots \\ = a \times b\).

如何求最大公約數?

歐幾里得輾轉相除法

原理:\(\gcd(a, \;b) = gcd(b,\;a\,\,mod\,\,b)\).

求兩個數的gcd,就等價於求一個數和對另一個數取模的gcd。這樣可以不斷縮小問題規模,直到發現一個數能被另一個數整除時,則除數就是原問題的最大公約數.

具體地,我們要求\(\gcd(a,\;b)\)
Step1:如果\(a < b\),則交換\(a, b\)的值.
Step2: 如果\(b = 0\),說明出現能夠整除的情況,則此時的\(a\)即為所求,為原問題的最大公約數,結束操作.
Step3: 如果\(b \not = 0\),我們此時就要求\(gcd(b, \;a\,\,mod\,\, b )\),循環執行Step1.

時間復雜度:理論上小於\(O(\log{n})\).

int gcd(int a, int b)
{  
    return b? gcd(b, a % b): a;       //運用三元運算符,可使代碼變得簡潔優美.
}

quiz 5 證明歐幾里得輾轉相除法的正確性.


\(a > b,\,r(a) = \{d\;|\;a\,\,mod\,\,d = 0\}, r(b) = \{d\;|\;b\,\,mod\,\,d = 0\}\). 易知,\(r(x)\)\(x\)的全體約數的集合.
\(d\)\(\,a,b\)的任意公約數,即\(d \in [r(a) \cap r(b)]\).

我們可以將\(a, b\)分別表示為\(a = k_1d, b = k_2d\). 即有,\(a - b = (k_1 - k_2)d\). 因為\(k_1 - k_2\)也為整數,所以\(d \;|\; (a - b)\).即\(d\)\(a, \,b\)的公約數約數同時也為\(b,\,a - b\)的公約數.

所以,\(r(a,\,b) = r(b,\,a - b) = r(b,\, a - 2 * b) = r(b,\,a - 3 * b) =\cdots = r(b,\,a \,\,mod\ b )\).因為它們的公約數集合相等,所以集合中的最大元素也一定相等,即最大公約數也一定相等.得證.

如何求最小公倍數?

我們求出以不大於\(O(\log{n})\)的復雜度求出gcd后,可以利用公式\(lcm = a \times b \,/\,gcd\,\)簡單求出最小公倍數.要注意的是,在編程中要寫成lcm = a / gcd * b,防止超出整型范圍.

tip 關於斐波那契數列的最大公約數

求斐波那契數列的相鄰兩項的最大公約數是輾轉相除法的最壞情況.因為斐波那契數列中,每兩項都是互質的.

\(F[n]\)為斐波那契數列的第\(n\)項。我們有\(\gcd{(F[a],F[b])}=F[\gcd{(a,b)}]\).
參考證明:https://www.douban.com/group/topic/33566582/.

二、取模理論

1.模

普遍地,我們可以這樣表達除法:$$a = \lfloor {\frac{a}{p}} \rfloor \times p + a % p$$

其中,\(p\)是除數,\(\lfloor {\frac{a}{p}} \rfloor\)是商,\(a \% p\) 是余數.

\(a \% p\)讀作\(a\)\(p\),即是\(a\) 在除以\(p\)下的余數.

  • 值域
    由於,模運算的本質實取余,所以模運算的結果一定落在\([0,\; p - 1]\).
    在一些問題中,若想使模運算的結果落在\([1, \;p]\), 可以(a - 1) % p + 1這樣計算.

2.隨時取模性質

在程序中,因為我們擔心一個式子中的某步運算結果會超出整型范圍,所以我們想到要對式子進行隨時取模.
一個運算中只包含加法和乘法,且如果運算結果只是要對\(p\)取模,那么我們可以在運算中隨時對\(p\)取模,結果不變.

例如,\((a + b \times c + d) \% p = ((a + b \times c \% p) \% p + d) \% p\).這樣就能盡量保證每步運算結果都不超過\(p\).

quiz 6 運用隨時取模性質,如何證明能被\(3\)整除的數的充要條件是各個數位上的數加和也一定能被\(3\)整除.

\(n = \overline{abcd} = a \times 1000 + b \times 100 + c \times 10 + d\).

\(n \% 3 \\ = (a \times 1000 + b \times 100 + c \times 10 + d) \% 3\\=(a \times 1000 \% 3+ b \times 100 \% 3+ c \times 10 \% 3 + d) \% 3 \\ = (a + b + c+ d) \% 3\)
證畢.

始編輯於2019/09/16 13:00
最新編輯於2019/11/22 11:56


免責聲明!

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



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