1. 程式人生 > 實用技巧 >機器學習 - 線性迴歸模型實戰 01

機器學習 - 線性迴歸模型實戰 01

1 ~ N 中與 N 互質的數互質是公約數只有1的兩個整數,叫做互質)的個數被稱為尤拉函式,記為ϕ(N)。
若在算數基本定理中,\(N=p_1^{\alpha_1}p_2^{\alpha_2}\cdots{p_m^{\alpha_m}}\),則有*式:

\[ϕ(N) = N\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\cdots\frac{p_m−1}{p_m} \]

證明:

  • 分解質因數N

  • 去掉N的質因數\(p_1 \cdots p_m\)所有的倍數,因為\(p_1,p_2\cdots{p_k}\) 一定不與N互質。

    \[\phi(N) = N - \lfloor{\frac{N}{p_1}}\rfloor - \lfloor\frac{N}{p_2}\rfloor \cdots \lfloor\frac{N}{p_k}\rfloor \]

  • 因為pi pj的公倍數會被去掉兩次,所以任取i,j pi *pj的倍數都要再加一次。即加上\(\frac{N}{pi \times pj}\)

  • 可見pi pj pk 的公倍數會被減去三次 又被加上三次,所以我們還要減去一次 即減去任取pi pj pk \(\frac{N}{pi \times pj \times pk}\)

  • 所以

    \[\phi(N) = N - \lfloor\frac{N}{p_1}\rfloor - \lfloor\frac{N}{p_2}\rfloor\ - \cdots \lfloor\frac{N}{p_m}\rfloor +\lfloor\frac{N}{p_1 p_2}\rfloor + \cdots + \lfloor\frac{N}{p_i p_j}\rfloor - \lfloor\frac{N}{p_1 p_2 p_3}\rfloor -\cdots - \lfloor\frac{N}{p_i p_j p_k}\rfloor \]

  • 可以發現*式展開即為上式。證明完畢

程式碼模板:

輸入k個數 輸出他們對應的尤拉函式

#include <iostream>
using namespace std;
int main(){
    int n ;
    cin >> n;
    int res =  n;
    //分解質因數模板
    for(int i =2;i <= n / i;i ++){//只要列舉到根號n就可以,因為一個數最多隻存在一個比根號n大的質因數
        if(n % i == 0) {//這裡符合條件的i只可能是質數,因為如果是合數,他肯定被之前列舉到他的一個質因數的時候就被除乾淨了
            res = res / i * (i - 1);//套用公式
            while(n % i == 0) n /= i;//找到一個質因數就把他除乾淨
        }
    }
    if(n > 1) res =res / n * (n - 1);//單獨處理最後一個大於根號n的質因數
    printf("%d\n",res);
    
    return 0;
}

演算法瓶頸主要是在分解質因數,分解質因數的時間複雜度為\(O(\sqrt{n})\),所以上述模板時間複雜度為\(O(\sqrt{n})\)

有的時候我們需要求出1到N中每一個數的尤拉函式,如果每個數都使用上述的演算法,那麼時間複雜度為\(O(n\sqrt{n})\)比較慢,可以用線性篩法,以\(O(n)\)的時間複雜度求出所有數的尤拉函式

程式碼:求1---N中所有數的尤拉函式之和

#include<iostream>
using namespace std;
const int N = 1000010;
typedef long long LL;
int phi[N];//記錄每個數的尤拉函式
int primes[N];//記錄質數
bool st[N];//記錄這個數是不是質數
int cnt;//質數的個數

LL get_eular(int n ){//改造線性篩法
    phi[1] = 1;
    LL res = 0;
    for(int i = 2;i <= n;i ++) {
        if(!st[i]) {//當前的數是質數
            primes[cnt ++] = i;
            phi[i] = i - 1; // 質數i與其互質的數就是1 --- i -1,所以就有i -1個互質的數
        }
        for(int j = 0; j < cnt && primes[j] <= n / i;j ++) {
            st[primes[j] * i] = true;
            if(i % primes[j] == 0) {//如果i % primes[j] == 0 說明primes[j]是i的最小質因數,也就是primes[k] * i的最小質因數
                phi[primes[j] * i] = phi[i] * primes[j];
                break;
            }
            phi[primes[j] * i] = phi[i] * (primes[j] - 1) ;
        }
    }
    for(int i = 1;i <= n ;i ++) {
        res += phi[i];
    }
    return res;
}


int main(){
    int n ;
    cin >> n ;
    
    LL res = get_eular(n);
    cout << res << endl;
    return 0;
}

線性篩法的詳細描述可以看我之前的部落格,現對線性篩法進行如下改造

  • 如果當前列舉到的數是質數那麼phi[i] = i - 1;質數i與其互質的數就是1 --- i -1,所以就有i -1個互質的數,即phi[i] = i -1;

  • 如果 i % primes[j] == 0 說明primes[j]是i的最小質因數,也是primes[k] * i的最小質因數(k > j) ,假設i分解質因數為 \(primes[j] ^ {\alpha_1} \times \cdots primes[m]^ {\alpha_m}\)

    \(\phi(i) = i \times (primes[j] - 1) / primes[j] \times \cdots \times(primes[m] - 1)/ primes[m]\)

    又因為x = primes[j] * i的分解質因數結果只比i分解質因數的結果多乘一個primes[j] 所以 \(\phi(x) = primes[j] \times i \times (primes[j] - 1) / primes[j] \times \cdots \times(primes[m] - 1)/ primes[m]\)

    所以\(\phi(x) = \phi(i * primes[j]) = \phi(i) \times primes[j]\)

    所以可以由phi[i] 得到phi[primes[j] * i].即phi[primes[j] * i ] = phi[i] * primes[j];

  • 如果i % primes[j] != 0 說明i中沒有primes[j]這個質因數,則x = primes[j] * i 的尤拉函式比i的尤拉函式式子多乘了一個質因數primes[j]的補償

    即$\phi(x) = \phi(primes[j] * i) = primes[j] \times i \times (primes[k] - 1) / primes[j] \times \cdots \times(primes[m] - 1)/ primes[m] \times (primes[j] - 1) / primes[j] $

    可以看到多了一個\(primes[j] \times (primes[j] - 1 )/ primes[j]\)的補償,化簡得到多了一個\(primes[j] - 1\)的補償

    所以\(\phi(x) = \phi(primes[j] * i) = \phi(i) \times (primes[j] - 1)\)

    所在i % primes[j] != 0的時候phi[i]也可以推出phi[i * primes[j]] 即phi[i * primes[j]] = phi[i] * (primes[j] - 1);