歐拉函數求法與歐拉篩法求素數

歐拉函數:

    歐拉函數定義:

       對於正整數n,歐拉函數Euler(n)是1到n-1中與n互質的數的個數,特別的,Euler(1) = 1,若n爲質數則有 Euler(n) = n - 1

     歐拉函數的兩種求法:

    1.由定義和常識可以知道對於任意一個素數n有 Euler(n) = n - 1,對於m = n ^ k,Euler(m)是非常好求解的,顯然,只有n的倍數纔是不滿足歐拉函數的定義的數,只要減去即可。得:Euler(m) = n ^ k - n ^ (k - 1)。

         另外,附加介紹以一點關於簡化剩餘系的概念,取定m > 0,若r mod m 中的每個數都與m互素,則稱r mod m是與m互素的剩餘類。從所有與模m互素的剩餘類中各取一數所組成的一組數稱爲簡化剩餘系。如 m = 5 有 1 mod 5, 2 mod 5, 3 mod 5, 4 mod 5,是與m互素的剩餘類。易知模m的一個簡化剩餘系中一共有Euler(m)個數,有定理:設(m1, m2) = 1,若x1,x2分別通過模m1,m2的一個簡化剩餘系,則m1x2 + m2x1通過m1m2的一個簡化剩餘系。通過這個定理我們可以得到Euler(m * n) = Euler(m) * Euler(n)。

          對於任意一個數,可以進行整數分解,分解成爲 m = n1 ^ x1 * n2 ^ x2 * ni ^ xi,這樣根據上面的兩個結論我們可以得到Euler(m) = Euler(n1 ^ x1) * ... * Euler(ni ^ xi) = ((n1 ^ x1) - (n1 ^ (x1 - 1))) * ... * ((ni ^ xi) - ni ^ (xi - 1)) = (n1 - 1) * (n1 ^ (x1 - 1)) * ... * (ni - 1) * (ni ^ (xi - 1)),根據這個式子我們可以推出如下的性質:設n爲m的質因數則有(m % n == 0) (1)若 (m / n) % n != 0 有E(m) = E(m / n) * (n - 1),這裏就是前面式子中每個質因數的前面的(ni - 1)。(2)若有(m / n) % n == 0的話,則是E(m) = E(m / n) *  n,對應的時候前面式子中每一質因數的xi次冪。

          對於上述的式子,可以得到第一種方法來求解歐拉函數。代碼如下:

int euler(int n){
    int ans = 1;
    for(int i = 2; i * i <= n; i ++){
        if(n % i == 0){
            n /= i;
            ans *= i  - 1;
            while(n % i == 0){
                n /= i;
                ans *= i;
            }
        }
    }
    if(n > 1)
        ans *= n - 1;
    return ans;
}

當然這裏給出歐拉函數的第二種揭解法,我們知道,任何一個數都可以分解成爲幾個素數乘積的形式,那麼比如1001 = 7 * 11 * 13,根據我們之前推出的,其實只要減去7,11,13這些質數的倍數,剩下的就都是與其互質的數,那麼這裏涉及到一個新的名詞——容斥定理,容斥定理在這裏的應用是去掉7的倍數,11的倍數,13的倍數,然後加上7和13的倍數,11和13的倍數,7和13的倍數,再減去7和11和13的倍數。

根據容斥定理,有以下代碼求解歐拉函數:

int euler(int n){
    int ans = n;
    for(int i = 2; i * i <= n; i ++){
        if(n %i == 0){
            ans -= ans / i;
            while(n % i == 0)
                n /= i;
        }
    }
    if(n > 1)
        ans -= ans / n;
    return ans;
}

歐拉篩法求素數:

    歐拉篩法求素數又稱爲線性篩法求素數,是一種將求一定範圍內的素數的時間複雜度控制在O(n)的一種算法,它跟埃拉特斯尼篩法求素數很像,做了一定的改進,我們知道,埃拉特斯尼篩法之所以沒有達到線性篩的水平是因爲它對於同一個合數,要重複操作他的質因子個數次,這是很浪費的,而歐拉篩法求素數則去掉了重複操作,它在當合數i可以整除質數時跳出對下一個數進行操作,比方說,當i = 4時,不會篩掉12這個數因爲12找到他的最小的質因數是2,12 = 2 * 6,6定義爲它相對最大的合數因子,那麼4不滿足這個條件,所以不在i =4 時篩掉12,而是在i = 6的時候篩掉12.這樣就避免了重複操作。

代碼如下:

bool IsPrime[1000010];
int Prim[1000010];
int  euler_prime(int n){
    int num = 0, j;
    for(int i = 2; i <= n; i ++){
        if(!IsPrime[i])
            Prim[num ++] = i;
        for(j  = 0; j < num; j ++){
            if(i * Prim[j] > n)
                break;
            IsPrime[i * Prim[j]] = true;
            if(i % Prim[j] == 0)
                break;
        }
    }
    //for(int i = 0; i < num; i ++){
    //    cout << Prim[i] << endl;
    //}
}


發佈了136 篇原創文章 · 獲贊 15 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章