機器學習 - 線性迴歸模型實戰 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);