1. 程式人生 > >關於歐拉函數與莫比烏斯函數等一系列積性函數的線性篩

關於歐拉函數與莫比烏斯函數等一系列積性函數的線性篩

出發點 get 我們 什麽 提升 討論 一點 每一個 ++i

為什麽要學習不同的篩法?

原因很簡單,因為通常當我們需要運用歐拉函數等一系列函數的時候,我們會采取提前預處理的方法來提高我們的效率。既然要提升效率,那麽我們就需要盡量用優秀一下的方法來完成我們的要求。

線性篩的出發點是什麽?

我們利用的最重要的性質就是它的積性。那麽積性是什麽?我們分為積性和完全積性。積形函數具有如下的性質:

\(F( a * b ) = F( a ) * F( b ) ( gcd( a , b ) = 1 )\)

而完全積性函數就是沒有互質這個限定條件

\(F( a * b ) = F( a ) * F( b )\)

那麽由歐拉函數,莫比烏斯函數的定義很容易的就可以得到它們是一個積性函數。因此,自然而然就可以想到,我們可以通過求出兩個互質的數 a , b 的函數值 來推出 a * b 對應的函數值

所以就有了一下這個篩法:


/*  線性篩求出莫比烏斯函數的值 
    利用積性函數的性質  */ 

mu[1] = 1;
for(int i = 1; i <= n; ++i)
{
    if(not_prime[i] == 0)
    {
        tot++;
        prime[tot] = i; mu[i] = -1; 
    }
    for(int j = 1; prime[j] * i <= n; ++j)
    {
        not_prime[prime[j] * i] = 1;
        if(i % prime[j] == 0
) { mu[prime[j] * i] = 0; break; } mu[prime[j] * i] = -mu[i]; } } /* 線性篩歐拉函數 */ void get_eular() { pnum = 0; for(int i = 2; i < MAX; i++) { if(!noprime[i]) { p[pnum ++] = i; phi[i] = i - 1
; } for(int j = 0; j < pnum && i * p[j] < MAX; j++) { noprime[i * p[j]] = true; if(i % p[j] == 0) { phi[i * p[j]] = phi[i] * p[j]; break; } phi[i * p[j]] = phi[i] * (p[j] - 1); } } }

現在我們來解釋一下這個代碼:
首先,這一系列函數分為兩個部分,質數與非質數。

那麽對於質數,我們應該先進行一次特殊處理。原因有兩個:

(1) 質數的計算方法不方便使用積性函數的性質,理應特殊處理(註:不同函數有對應的方法)

(2) 質數處理了以後,我們要通過它去求得更多的函數值。

對於非質數,運用積性函數的性質進行計算

那麽則是循環語句中最關鍵的幾句話

你每循環到一個 i ,就應該算出 i 的倍數的對應的函數值。又因為質數的性質,只有當 i 是一個質數的倍數的時候它們才可能不互質。所以這是一個臨界條件。

既然這樣,我們再討論一下為什麽能夠break?

我們擔心的無非就是會不會有遺漏?顯然我們不需要擔心這一點,原因很簡單:

每一個數一定會被它最小的質因數給計算到,而剛好在計算以後 ———— break;

所以不會有遺漏的情況

因此有了這種常用的線性篩法

關於歐拉函數與莫比烏斯函數等一系列積性函數的線性篩