1. 程式人生 > 其它 >【數學】質數、合數、篩質數、分解質因數

【數學】質數、合數、篩質數、分解質因數

  1. 質數和合數是針對所有大於 1 的 “自然數” 來定義的(所有小於等於1的數都不是質數)。

  2. 所有小於等於 1 的整數既不是質數也不是合數.

  3. 質數的判定——試除法

  • “d|n”“d|n”代表的含義是 d 能整除 n ,(這裡的 “|”“|” 代表整除)

  • 一個合數的約數總是成對出現的,如果 \(d|n\) ,那麼 \((n/d)|n\),因此我們判斷一個數是否為質數的時候,只需要判斷較小的那一個數能否整除 \(n\) 就行了,即只需列舉 \(d<=(n/d)\),即 \(d∗d<=n,d<=sqrt(n)\)

  • \(sqrt(n)\)這個函式執行的時候比較慢.
    一般這樣寫:

  bool is_prime(int x)
  {
      if (x < 2) return false;
      for (int i = 2; i <= x / i; i ++ )
          if (x % i == 0)
              return false;
      return true;
  }
  1. 分解質因數——試除法(算數基本定理)
    任何一個大於1的自然數 ,如果N不為質數,都可以唯一分解成有限個質數的乘積
    \(N=P_1^{a1}P_2^{a2}···P_n^{an}\) ,這裡 \(P_1<P_2<···<P_n\)
    且均為質數,其諸指數是正整數。
    這樣的分解稱為\(N\)的標準分解式。
  • 特別要注意——分解質因數與質因數不一樣,分解質因數是一個過程,而質因數是一個數.
  • 一個合數分解而成的質因數最多隻包含一個大於 sqrt(n)sqrt(n) 的質因數(反證法,若 nn 可以被分解成兩個大於 sqrt(n)sqrt(n) 的質因數,則這兩個質因數相乘的結果大於 nn ,與事實矛盾)
  • 質因子(質因數)在數論裡是指能整除給定正整數的質數。根據算術基本定理,不考慮排列順序的情況下,每個正整數都能夠以唯一的方式表示成它的質因數的乘積。
  • 兩個沒有共同質因子的正整數稱為互質。因為 11 沒有質因子,11 與任何正整數(包括 11 本身)都是互質。
  • 只有一個質因子的正整數為質數。
  • 當列舉到某一個數 \(i\) 的時候,\(n\) 的因子裡面已經不包含 \([2,i−1]\) 裡面的數(已經被除乾淨了),如果 \(n%i==0\),則 \(i\) 的因子裡面也已經不包含 \([2,i−1]\) 裡面的數,因此每次列舉的數都是質數。
  void divide(int x)
  {
      for (int i = 2; i <= x / i; i ++ )
          if (x % i == 0)
          {
              int s = 0;
              while (x % i == 0) x /= i, s ++ ;
              cout << i << ' ' << s << endl;
          }
      if (x > 1) cout << x << ' ' << 1 << endl; // 大於sqrt(n)的數
      cout << endl;
  }
  1. 篩質數(樸素篩法)
  • 步驟:把 [2,n−1][2,n−1] 中的所有的數的倍數都標記上,最後沒有被標記的數就是質數.
  • 原理:假定有一個數 \(p\)未被 \([2,p−1]\) 中的數標記過,那麼說明,不存在 \([2,p−1]\) 中的任何一個數的倍數是 \(p\) ,也就是說 \([2,p−1]\)中不存在 \(p\) 的約數,因此,根據質數的定義可知:\(p\) 是質數.
  • 調和級數:當 \(n\) 趨近於正無窮的時候,\(1/2+1/3+1/4+1/5+…+1/n=lnn+c1/2+1/3+1/4+1/5+…+1/n=ln⁡n+c\)\(c\) 是尤拉常數,約等於 0.577 左右)
  • 時間複雜度:約為 \(O(nlogn)\)(注:此處的 \(log\) 特指以 \(2\) 為底)
  1. 埃拉託斯特尼篩法(埃氏篩)
  • 要得到自然數\(n\)以內的全部素數,必須把不大於\(\sqrt{n}\)的所有素數的倍數剔除,剩下的就是素數。
  • 質數定理:\(1~n\) 中有 \(\frac{n}{\ln n}\)個質數.
  • 時間複雜度:\(O(nlog(logn))\)
void get_primes(int n)
{
    for(int i = 2; i <= n; i ++ )
    {
        if(!st[i]) primes[cnt ++] = i;
        else continue; //埃氏篩優化
        for(int j = 2 * i; j <= n; j += i) st[j] = true;
    }
}
  1. 線性篩(尤拉篩)
  • \(n\approx 10^6\),線性篩和埃氏篩的時間效率差不多,若 \(n\approx 10^7\),線性篩會比埃氏篩快了大概一倍。
  • 核心:\(1~n\)內的合數 \(p\) 只會被其最小質因子篩掉。
  • 原理:\(1~n\) 之內的任何一個合數一定會被篩掉,而且篩的時候只用最小質因子來篩,然後每一個數都只有一個最小質因子,因此每個數都只會被篩一次,因此線性篩法是線性的.
  • 列舉到 \(i\) 的最小質因子的時候就會停下來,即 if (i % primes[j] == 0) break;
  • i % primes[j] != 0時,primes[j] 一定小於 i 的最小質因子,primes[j] 一定是 primes[j] * i 的最小質因子.
  • i % primes[j] == 0 時,primes[j] 一定是 i 的最小質因子,而 primes[j] 又是 primes[j] 的最小質因子,因此 primes[j]primes[j] * i的最小質因子.
  int primes[N], cnt;     // primes[]儲存所有素數
  bool st[N];         // st[x]儲存x是否被篩掉(非質數被篩掉,剩下的是質數)

  void get_primes(int n)
  {
      for (int i = 2; i <= n; i ++ )
      {
          if (!st[i]) primes[cnt ++ ] = i;
          // j < cnt 不必要,因為 primes[cnt - 1] = 當前最大質數
          // 如果 i 不是質數,肯定會在中間就 break 掉
          // 如果 i 是質數,那麼 primes[cnt - 1] = i,也保證了 j < cnt
          for (int j = 0; primes[j] <= n / i; j ++ )
          {
              st[primes[j] * i] = true;
              if (i % primes[j] == 0) break;
          }
      }
  }