歐拉函數
歐拉函數的定義
對於正整數n,小於且與n互質的正整數(包括1)的個數,記作\(\phi(N)\)
易得,\(\phi(質數)=質數-1\)
同時規定,\(\phi(1)=1\)
歐拉函數的計算
就拿8來舉例,小於8且與8互質的正整數有1,3,5,7,共4個。
因而,\(\phi(8)=4\)
那\(\phi(120)\)的值是多少?
\(\phi(120)=120\times(1-\frac{1}{2})\times(1-\frac{1}{3})\times(1-\frac{1}{5})=32\)
為啥是這樣子計算呢?
首先,要承認的兩點是,
- 給定一個數字n,在n這個范圍內(1比較特殊)去求n的互質的數字是不包含n的任意的因子。
- 用一個數a去除去另一個數b,會得到a關於b的余數,可按余數為0和余數為非0將數a分成兩類。
比如120中3的倍數有40個(余數為0),非3的倍數有80個(余數為1和2)。
\(\phi(120)=120\times(1-\frac{1}{2})\times(1-\frac{1}{3})\times(1-\frac{1}{5})=32\)
所以整個式子的意思就是先在120里取非2倍數的數,再在這部分里面取非3倍數的數,然后再在這倍數里面取非5倍數的數。(乘法原理)
歐拉函數的代碼實現
單求一個數字n的歐拉函數——分解質因數算法
首先,我們需要將給定的n給分解成質因子相乘。
這個時候就需要用到試除法來分解質因數
for(int i=2;i*i<=n;i++)
if(n%i==0)
{
pri.push_back(i);
while(n%i==0)
n/=i;
}
if(n>1) pri.push_back(n);
這里for中i*i<=n的循環條件必然會使得最終剩下有且只有一個沒有除干凈的數字(可用反證法證明,假設有終止的時候n仍可以用兩個不同的因子來組成,那么循壞的i是卡在較小的那個數字上,而較小的數字乘上它本身是固然會小於這個較小的數字去乘上一個較大的數字的,而這時也就意味着循環沒有被終止,這個較小的因子最終也會被除去)或者只剩下1。
題目AcWing 873. 歐拉函數

#include <bits/stdc++.h>
#define MEM(a,x) memset(a,x,sizeof(a))
#define W(a) while(a)
#define gcd(a,b) __gcd(a,b)
#define pi acos(-1.0)
#define PII pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define lowbit(x) (x&-x)
using namespace std;
int t,n;
vector<int > pri;
int main()
{
cin>>t;
while(t--)
{
pri.clear();
cin>>n;
int ans=n;
for(int i=2;i*i<=n;i++)
if(n%i==0)
{
pri.push_back(i);
while(n%i==0)
n/=i;
}
if(n>1) pri.push_back(n);
for(int i=0;i<pri.size();i++)
ans=ans/pri[i]*(pri[i]-1);
cout<<ans<<endl;
}
return 0;
}
求1到n中所有數字的歐拉函數和——篩法——歐拉篩
前置
根據算術基本定理,我們可以知道任意的一個數n,有\(n=p_{1}^{k_1}\times p_{2}^{k_2}\times p_{3}^{k_3}\times...\times p_{n}^{k_n}\),其中\(p_{i}\)表示質數。
由於我們前面的算法的實際情況和原理,質因子所搭配的指數\(k_i\)對我們的答案並沒有起到作用。
- 若n為質數,\(\phi(n)=n-1\)
- 如果一個數n可以用一個質數p和未知性質的數i相乘來表示的話,分兩種情況,即有\(n=i\times p\)
- 若
i%p==0成立,說明說x包含p這個質因子,而這時我們要計算的時\(\phi(i\times p)\),那么借由上文所提及到質因子所搭配的指數\(k_i\)對我們的答案並沒有起到作用的理論基礎,我們可以簡單將\(\phi(i)\)理解成是一個周期,而去把p理解成周期數,因而有,\(\phi(i\times p)=\phi(i)\times p\) - 若
i%p==0不成立,則說明x是不包含p這個質因子,也就是在求\(i\times p\)的歐拉函數時,是有\(\frac{p-1}{p}\)份符合要求的和\(\frac{1}{p}\)不符合要求的,而換個角度和基於已經算好\(\phi(i)\)的基礎上,我們就有\(\phi(i\times p)=\phi(i)\times (p-1)\),這個時候我們可以簡單把\(\phi(i)\)理解成是一個周期數,而\(p-1\)是單個周期的量。 - 關於\(\phi(i)\)還是\(\phi(p)\),要采取的是\(\phi(i)\),因為i和所求的數僅僅差一個質數p,而p和所求的數差了一個相對未知的i,后者的做法很容易造成信息的丟失。
- 若
題目AcWing 874.篩法求歐拉函數

#include <bits/stdc++.h>
#define MEM(a,x) memset(a,x,sizeof(a))
#define W(a) while(a)
#define gcd(a,b) __gcd(a,b)
#define pi acos(-1.0)
#define PII pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define lowbit(x) (x&-x)
using namespace std;
const int N = 1e6+10;
int cntprime,phi[N],nprime[N],prime[N];
bool used[N];
int Euler(int n)
{
phi[1]=1;
for(int i=2;i<=n;i++)
{
if(!nprime[i])
prime[cntprime++]=nprime[i]=i,phi[i]=i-1;
for(int j=0;j<cntprime;j++)
{
if(prime[j]*i>n)
break;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
nprime[i*prime[j]]=prime[j];
break;
//因為i已經包括了prime[j],那么i*prime[j+1]必然能被prime[i]整除,
//而就沒有必要再往上面跑了
//這里這個break保證O(n)的算法復雜度
}
else
{
nprime[i*prime[j]]=prime[j];
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
}
int main()
{
int n;
cin>>n;
Euler(n);
ll ans=0;
for(int i=1;i<=n;i++) ans+=phi[i];
cout<<ans;
return 0;
}
