歐拉函數
歐拉函數就是指:對於一個正整數n,小於n且和n互質的正整數(包括1)的個數,記作φ(n) 。
歐拉函數的通式:φ(n)=n*(1-1/p1)(1-1/p2)(1-1/p3)*(1-1/p4)……(1-1/pn),其中p1, p2……pn爲n的所有質因數,n是不爲0的整數。φ(1)=1(唯一和1互質的數就是1本身)。
對於上述的通式一定要牢記在心,因爲這是計算歐拉函數最重要的一步
廢話少說,先上代碼:
int euler(int n)
{
int ans=n;
for(int i=2;i*i<=n;i++){
if(n%i==0){
ans-=ans/i; // 這一步就是對應歐拉函數的通式
//這一個語句是爲了保證完全消除我們剛纔得到的那個i因子。確保我們下一個得到的i是n的素因子。
while(n%i==0){
n/=i;
}
}
}
//這個語句是爲了保證我們已經除完了n的所有的素因子,有可能還會出現一個我們未除的因子,
//如果結尾出現n>1 ,說明我們還剩一個素因子木有除。
if(n>1)ans-=ans/n;
return ans;
}
但是我們一般做的題當然不會這麼簡單啊~~來點稍微難一點點的。。。
如果我們要求的數比較多,如果一個一個求那麼很容易就超時,所以我們自然而然就想到——打表
我們先來一個最樸素的打表
void euler()
{
p[1]=1;
for(int i=2;i<=MAXN;i++){
int n=i;
p[i]=i;
for(int j=2;j*j<=n;j++){
if(n%j==0){
p[i]=p[i]/j*(j-1);
while(n%j==0) n=n/j;
}
}
if(n>1) p[i]=p[i]/n*(n-1);
}
for(int i=2;i<MAXN;i++)
p[i]+=p[i-1];
}
這種打表方法並不是很理想。。。。
下面推薦 兩種較快的打表方法:
ps:這種好像稍微快那麼一點點~
void euler()
{
E[1]=1;
for(int i=2;i<maxn;i++)
E[i]=i;
for(int i=2;i<maxn;i++){
if(E[i]==i)
for(int j=i;j<maxn;j+=i){
E[j]=E[j]/i*(i-1);
}
}
}
第二種:
void euler()
{
for(int i=2;i<maxn;i++){
if(!E[i])
for(int j=i;j<maxn;j+=i){
if(!E[j])E[j]=j;
E[j]=E[j]/i*(i-1);
}
}
}
上述打表方法的思想和最初的是差不多的。但是它進行了優化,所以比較快。
我們逐步分析一下:
首先,在這裏我們枚舉的是素因子。因爲素因子比較少,如果枚舉素因子的話肯定會大大優化複雜度?
那麼,我們如何保證我們得到的就一定是個素因子呢?這就是我們if語句的作用,例如在第一種方法中,我們在第二個for 循環中就是把所有數,除以素因子。這樣得到的複雜度一定比枚舉每個數,然後找素因子(最開始那個打表法)這種打表要優化的多。