ACM數論之旅7---歐拉函數的證明及代碼實現(我會證明都是騙人的╮( ̄▽ ̄)╭)


歐拉函數,用φ(n)表示

歐拉函數是求小於等於n的數中與n互質的數的數目

 

 

 

辣么,怎么求哩?~(~o ̄▽ ̄)~o

 

可以先在1到n-1中找到與n不互質的數,然后把他們減掉

 

比如φ(12)

把12質因數分解,12=2*2*3,其實就是得到了2和3兩個質因數

然后把2的倍數和3的倍數都刪掉

2的倍數:2,4,6,8,10,12

3的倍數:3,6,9,12

 

本來想直接用12 - 12/2 - 12/3

但是6和12重復減了

所以還要把即是2的倍數又是3的倍數的數加回來 (>﹏<)

所以這樣寫12 - 12/2 - 12/3 + 12/(2*3)

這叫什么,這叫容斥啊,容斥定理聽過吧

比如φ(30),30 = 2*3*5

所以φ(30) = 30 - 30/2 - 30/3 - 30/5 + 30/(2*3) + 30/(2*5) + 30/(3*5) - 30/(2*3*5)

 

但是容斥寫起來好麻煩( ̄. ̄)

有一種簡單的方法

φ(12)   =   12*(1 - 1/2)*(1 - 1/3)                 =   12*(1 - 1/2 - 1/3 + 1/6)

φ(30)   =   30*(1 - 1/2)*(1 - 1/3)*(1 - 1/5)   =   30*(1 - 1/2 - 1/3 - 1/5 + 1/6 + 1/10 + 1/15 - 1/30)

你看( •̀∀•́ ),拆開后發現它幫你自動幫你容斥好

 

所以φ(30)的計算方法就是先找30的質因數

分別是2,3,5

然后用30* 1/2 * 2/3 * 4/5就搞定了

 

順便一提,phi(1) = 1

代碼如下:

 1 //歐拉函數
 2 int phi(int x){
 3     int ans = x;
 4     for(int i = 2; i*i <= x; i++){
 5         if(x % i == 0){
 6             ans = ans / i * (i-1);
 7             while(x % i == 0) x /= i;
 8         }
 9     }
10     if(x > 1) ans = ans / x * (x-1);
11     return ans;
12 }

 

 

(phi就是φ的讀音)

 

 

機智的代碼,機智的我(。・`ω´・)

 

 

這個的復雜度是O(√n),如果要你求n個數的歐拉函數,復雜度是O(n√n),這也太慢了

 

 

有更快的方法

跟埃篩素數差不多

 1 #include<cstdio>
 2 const int N = 100000 + 5;
 3 int phi[N];
 4 void Euler(){
 5     phi[1] = 1;
 6     for(int i = 2; i < N; i ++){
 7         if(!phi[i]){
 8             for(int j = i; j < N; j += i){
 9                 if(!phi[j]) phi[j] = j;
10                 phi[j] = phi[j] / i * (i-1);
11             }
12         }
13     }
14 }
15 int main(){
16     Euler();
17 }

 

(Euler就是歐拉)

 

 

另一種,比上面更快的方法

需要用到如下性質

p為質數

1. phi(p)=p-1   因為質數p除了1以外的因數只有p,故1至p的整數只有p與p不互質 

2. 如果i mod p = 0, 那么 phi(i * p)=phi(i) * p         (我不會證明)

3.若i mod p ≠0,  那么 phi( i * p )=phi(i) * ( p-1 )   (我不會證明)

(所以我說我會證明都是騙人的╮( ̄▽ ̄)╭)

 

代碼如下:

 1 #include<cstdio>
 2 using namespace std;
 3 const int N = 1e6+10 ;
 4 int phi[N], prime[N];
 5 int tot;//tot計數,表示prime[N]中有多少質數 
 6 void Euler(){
 7     phi[1] = 1;
 8     for(int i = 2; i < N; i ++){
 9         if(!phi[i]){
10             phi[i] = i-1;
11             prime[tot ++] = i;
12         }
13         for(int j = 0; j < tot && 1ll*i*prime[j] < N; j ++){
14             if(i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j]-1);
15             else{
16                 phi[i * prime[j] ] = phi[i] * prime[j];
17                 break;
18             }
19         }
20     }
21 }
22  
23 int main(){
24     Euler();
25 }

 

 

 

 

最后說下

 

a^b % p  不等價  (a%p)^(b%p) % p

因為

a^φ(p) ≡ 1 (mod p)

 

所以

a^b % p  =  (a%p)^(b%φ(p)) % p

(歐拉函數前提是a和p互質)

歐拉求余1

如果p是質數

直接用這個公式

歐拉求余2

 

 

 

 

 

機智的代碼,機智的我(。・`ω´・)

 

 

 

 ///////////////////////////////////////////////

2016年7月23號

我的天哪,我又發現了一個新公式,貌似可以擺脫a和p互質的束縛,讓我們來命名為:超歐拉取模進化公式

 

 

這是歷史性的一刻,媽媽再也不用為a和p不互質而擔心了= =

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM