1. 程式人生 > >莫比烏斯反演入門(詳解)

莫比烏斯反演入門(詳解)

最近在學習莫比烏斯反演的知識,看了很多網上的部落格,大多都是堆了很多公式但是講得不夠詳細,不太容易理解,這裡推薦一個寫得很詳細的部落格:

這個文章主要講一下ACM中1個常用的莫比烏斯反演公式,看到很多部落格上面公式是有,但是都沒證明,《組合數學》上的證明又沒看懂,

就自己想了種證明方法,覺得比《組合數學》的證明簡單些,就寫一下,希望對初學莫比烏斯反演的同學有幫助。

PS:下面公式出現的sigma是累加,另外建議大家看的時候 把公式在紙上寫出來!

一:什麼是莫比烏斯反演

簡單點的說,就是先給出一個函式 F(n) ,然後再由 F(n)定義一個新函式 G(n)

其中   G(n) = sigma(F(d)) (其中d被“包含”於n)  

然後 現在我們不知道 F(n)的值 , 卻知道 G(n), 接著我們就可以通過 反演由G(n)反向得到F(n)

什麼叫 (其中d被“包含”於n) ?以及怎麼理解反演? 通過下面的幾個例子說明

例1:

我們直接定義 G(n)=sigma(F(i)) (1<=i<=n)    {這裡的每個F(i),相對於G(n)實際上就是一種包含關係了!!}

然後我們現在已經知道 G(n)=n*(n+1)/2;

接下來 我們要通過 G(n)反向得到F(n) 的過程,就是反演

當然,這個問題很簡單,很容易都可以看出來 F(n)=n ~~

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

例2:

我們先令  S,X 都表示集合 比如 S={1,4,6} X={2} 等  並令|S|表示 S中元素的個數

接著定義 集合上的函式 F(S)   /*具體怎麼定義不用管,我們只需要知道有這麼一個關於集合的函式F就好了 :) */ 

然後再定義 G(S)=sigma(F(X)) (其中X是S的子集)   {這裡也是一種包含關係,集合的包含!!}

接著我們不知道F(S),想通過G(S) 來得到 F(S)

這個問題相對於例1就複雜多了,但實際上我們已經有現成的關於集合包含的莫比烏斯反演公式了  :)

F(S)=sigma((-1)^(|S|-|X|) * G(X))  (其中X是S的子集)

是不是感覺有點神奇?

大家可以自己寫個程式來驗證一下。

下面就是我的驗證程式:

我定義 F(S)=|S| , 然後先 計算出 F(S) ,接著 計算出 G(S) , 然後 比較由G(S)反演得到的 F(S)和 |S| 的大小

下面是 我的程式

複製程式碼
#include <iostream>
#include <math.h>
using namespace std;

#define base 10
#define REP(i,n) for(int i=0;i<(n);i++)
int F[1<<base],G[1<<base];
// 集合用二進位制表示 base表示集合最多10個元素

int Cal(int x){ // 計算 |x|
    int sum=0;
    while(x) sum+=(x&1),x/=2;
    return sum;
}

int main(){
    REP(S,1<<base) F[S]=Cal(S); // 計算出最開始的F(S)
    REP(S,1<<base){   // 計算G(S)
        G[S]=0;
        for(int X=S;X;X=(X-1)&S) G[S]+=F[X]; //用X遍歷S集合
    }
    REP(S,1<<base){     // 計算反演的 F(S)
        F[S]=0;
        for(int X=S;X;X=(X-1)&S)
            F[S]+=(int)pow(-1,Cal(S)-Cal(X))*G[X];
    }
    bool flag=1;    // 驗證一下
    REP(S,1<<base)
        if(F[S]!=Cal(S)) flag=0;
    if(flag) cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
}
複製程式碼

最後得到的結果 當然是 YES 咯!:)

關於這個 反演公式 的證明,先不要著急,看完文章過後,你自己都能摸索著證明了!!

現在先大概理解反演是個什麼就行了!!

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

例3:

先令  d|n 表示 d能整除n  比如 2|4 (=.=)

定義 關於 整數 的函式 F(n)

然後 定義 G(n)=sigma(F(d)) (其中d|n)  

上面的這種包含關係就更復雜了,只有當d是n的因子的時候,F(d)才會被包含在G(n)中。

不過這種 包含關係 在 ACM中遇到的最多,所以我會詳細講一下這種型別的 反演。

相信明白了這個過後,例2的反演也能夠自己證明了。

具體的講解見下一章節 :)

二:一類反演

這個一類反演就是例3中的那一類咯= =

我先直接給出結論吧

原式 :  G(n)=sigma(F(d))  (其中d|n)  

反演公式:   F(n)=sigma(U(n/d)*G(d))   這裡U是一個函式,他是每一項 G(d) 的係數,他的定義見下面

  (強烈建議關於U的定義這一段可以先跳過,先認為他是G的係數就行了,可以跳到下面紅字位置)

    (1).U是一個關於整數的函式

    (2).U[x] = 1 當且僅當 x能夠分解成偶數個不同質數的乘積  (其中1不能被分解,所以1的分解出的質數個數是0,所以U[1]=1)

    (3).U[x] = -1 當且僅當 x能夠分解成奇數個不同質數的乘積

    (4).U[x] = 0  除開(2),(3)的其他情況

    看上面關於U的定義可能有點看暈了,通俗一點的說

對於一個 x , 分解因式過後 有  x=(p1^e1)*(p2^e2)...*(pr^er)

如果 ei中(1<=i<=r)有一個數ei大於1  那麼  U[x] = 0;

不然的話   U[x] = (-1)^r

依舊來兩個例子(我最喜歡舉例子了 = =)

U[1]=1;定義中的說明

U[2]=-1;    分解式 2=2;

U[6]=1;   分解式 6=2*3

U[9]=0;   9=3^2; 出現了e>1

U[12]=0;   12=2^2*3;

跳到這裡 :)

上面就是關於這類反演公式的定義,不要頭暈= =,繼續往下看吧

在證明之前,我們先想一下,為什麼反演公式會是   F(n)=sigma(U(n/d)*G(d))   這樣的型式?

依舊通過例題來找規律 (^ ^)

我們令 n=6;

那麼 在計算 F(6)的時候,我們會用到 G(1) G(2) G(3) G(6)

我們考察者4個G

G(1) = F(1)

G(2) = F(1)+F(2)

G(3) = F(1)+F(3)

G(6) = F(1)+F(2)+F(3)+F(6)

觀察上面可以發現 每個 G(n)都是由一些F(d)累加得到的 

當我們需要逆向有G得到F(n)時,只需要將一些 與 F(n) 有關的 G進行容斥!!!!! 最終組合得到F(n)!!!

比如 F(6) = G(6)-G(2)-G(3)+G(1) !!!!

有些神奇!! 不過這類莫比烏斯反演的實質也就是容斥原理的應用!!

那麼我們現在知道為什麼 這類反演公式會是 這個形式了,而且對其原理也有了更深的理解,現在該想一想公式的細節了。

既然我們知道要得到 F(n) ,只需要將與其相關的 G進行容斥就可以,那麼剩下的問題就是每個G的係數!!!

我們以 求解 F(6)為例子來說明 ,並定義一個係數函式 H(d,n).

其中 H(d,n)表示 求解F(n)時,G(d)的係數  (其中d|n)

所以可以得到這個式子 F(6) = H(6,6)*G(6)+H(2,6)*G(2)+H(3,6)*G(3)+H(1,6)*G(1)

我們用 a,b,c,d分別替代 四個H(6,6),H(2,6),H(3,6),H(1,6),並且把對應的G用F表示出來,得到

F(6)=a*(F(6)+F(3)+F(2)+F(1))+b*(F(2)+F(1))+c*(F(3)+F(1))+d*F(1),再變形一下,又有

F(6)*(a-1)+F(3)*(a+c)+F(2)*(a+b)+F(1)*(a+b+c+d)=0,把F(6),F(3),F(2),F(1)當作不同的元,則得到了下面的方程組!!!

a-1==0

a+c==0

a+b==0

a+b+c+d==0

由此發現,四個未知數,四個方程,只需要解出方程,就能知道對於G的係數。

再深入的想一下,對於每個 F(n),假設他的因子數為,m,則通過這種方式,總能設出m個未知數,m個方程,

這樣總能找到解,而這也為莫比烏斯反演的可能性作出瞭解釋!!

現在我們要證明一個結論,即使H(a,b)==H(1,b/a)!!這個結論很重要,具體分析見下 :)

我們以求解 F(8)為例子,與F(8)相關的 H 有 ,H(8,8),H(4,8),H(2,8),H(1,8)

F(8)=H(8,8)*G(8)+H(4,8)*G(4)+H(2,8)*G(2)+H(1,8)*G(1)

首先看 H(8,8),其值可以直接確定,因為把F(8)當作元的話,左邊一個F(8),而在右邊F(8)只在G(8)中出現,所以H(8,8)==1

同理 對於 F(n),其G(n)的係數H(n,n)==1,所以H(8,8)==H(1,1)

再來看H(4,8),,首先想,F(4)在哪些地方出現,發現 在G(8)和G(4)出現,因為左邊不含F(4),而前面G(8)的係數又已經確定,

所以這裡H(4,8)*G(4)的作用就是為了抵消前面G(8)的代換中,出現的F(4),所以 H(4,8)==-H(8,8)==-H(2,2)==H(1,2),{H(1,2)==-H(1,1)請大家自己驗證一下}

同理對於H(2,8),他是為了抵消前面在G(8)和G(4)中出現的F(2),所以H(2,8)相當於受到H(4,4)和H(2,4)的影響(假設這個結論對n==4也成立,H(2,4)==H(1,2)),

所以H(2,8)==H(1,4)

找到規律過後,總結一下,假設n的因子有 d1,d2,d3...dm 其中 d1>d2>d3...>dm

我們依次確定H(di,n)的值,當我們在確定H(di,n)的值時,前面的值已經確定,即H(dj,n)(j<i)的值已經確定,

H(di,n)會受到前面一些H(dj,n)的影響,當且僅當 dj>di且 di|dj 。

假設 H(a,b)==H(1,b/a)對前面的 H(dj,n)和 所有的H(k,m)其中m<n 已經成立(首先對於H(n,n)已經成立),那麼有  H(dj,n)==H(1,n/dj)==H(dj/di,n/di)

這樣就把前面對H(di,n)造成影響的H由 H(dj,n)轉為了 H(dj/di,n/di) ,所以H(di,n) == H(1,n/di)

既然 H(a,b) 都可以 寫成 H(1,b/a) , 於是我們把H的第一個元素略去,簡寫為 H(x)

說到這裡,就可以把H和U聯絡起來了,其實 U(x) = H(x) = H(1,x)  

再來,我們就可以給U(x)賦予一個更具體的意義, U(x)表示在計算 F(x)時,G(1)的係數!!(因為U(x)==H(1,x))

接下來,我們來嘗試一下,如何用上面那個U(x)的新意義,來計算U(x)的值!!

首先需要明確2點! 

一是G(x)中,一定包含一個F(1),因為 1|x

二是,F(1)==G(1)

(0).如果 x==1

因為 F(1)==G(1) 所以 U[1]=1;

(1).假設 x 是一個 質數   

F(x) = U(1)*G(x)+U(x)*G(1)

帶入U(1) == 1,  因為G(x)中含有一個F(1),而左邊不含F(1),所以我們需要利用G(1)來消去F(1)

所以得到 U(x)=-1

(2).假設 x 可以寫成2個不同質數的乘積  x=p*q

那麼  F(x)=U(1)*G(pq)+U(q)*G(p)+U(p)*G(q)+U(x)*G(1)

這裡 U(1),U(p),U(q) 就是前面2種情況

帶入係數,因為左邊沒有 F(1),所以為了抵消右邊的F(1),我們需要令 U(x)=1;

(3).假設 x 可以寫成3個不同質數的乘積  x=p*p1*p2  我們令  z = p1*p2

F(x) = U(1)*G(pz)+U(z)*G(p)+U(p)*G(z)+U(x)*G(1);

其中 U(1),U(p),U(z) 分別為前面幾種情況,帶入過後 ,為抵消F(1)   得到 U(x)=-1

由此可以相同的方式向下遞推,得到第一條結論

如果 x = p1*p2...*pr , 其中pi是互異的質數,那麼 U[x] = (-1)^r  -----------------------   1!!

(4).假設 x 可以寫成一個質數的平方  x=p^2

F(x) = U(1)*G(x)+U(p)*G(p)+U(x)*G(1)

帶入係數 得到 U(x)=0;

(5).假設 x 可以寫成一個質數的三次方  x=p^3

F(x) = U(1)*G(x)+U(p)*G(p^2)+U(p^2)*G(p)+U(x)*G(1)

帶入係數後   U(x)=0;

由此可用相同方式向下遞推,得到第二條結論

如果 x = p^e (e>1) U[x] = 0; -------------------------- 2!!

(6).假設 x 可寫成 x = p^e*q  其中p,q為不同質數,e>1

F(x) = U(1)*G(x)+U(q)*G(p^e)+U(p^e)*G(q)+U(x)*G(1)

帶入係數後   U(x) = 0;

由此可繼續向下遞推,得到第二條結論的加強版!!

如果 x = p^e*z 其中p為質數, z為任意數,e>1  那麼  U[x] = 0 ----------------------2!!

由此,我們得到了 U[x] 的計算方法!!即是U定義中給出的那樣!!(沒看定義的同學此時再跳回去看吧)

三:應用

得到了公式,也知道了他是怎麼來的,現在就用一個應用來加深理解吧  :)

首先我們要給出 第二部分 中那個公式的另外一種形式 = =  我們把它稱為形式二吧~

原式 :  G(n)=sigma(F(d))  (其中n|d,d<=N)  

反演公式:   F(n)=sigma(U(d/n)*G(d))  (其中n|d,d<=N) 這裡U[x]的計算方式和上面的相同!!

注意上面的 n|d 和 d/n  和上面是相反的

證明方法和上面差不多,大致說一下

還是先設定一個係數函式 H(d,n) 表示求解 F(n)時 G(d)出現的次數,

接著 用與上面類似的方法變化H(d,n) 為 H (d/n,1) ---> H(x,1)

則聯絡 U(x) == H(x,1)  表示  在計算 F(1)時,G(x)的係數

以 x 為質數為例子,由於 G(1)=F(1)+F(2)....+F(N)

F(1) = G(1)+U(2)G(2)...+U(x)G(x)...+U(N)G(N)

因為 x 為質數 所以 F(x)這一項 只在G(1)裡面出現了一次,而其他地方只會在 G(x)出現

所以我們需要讓 U(x)=-1 來抵消 F(x)

剩下的步驟就和上面差不多了,分類討論一下,就可以求出這種情況下的U的計算方式,和上面相同!!

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下來就真正的開始演示怎麼用 莫比烏斯反演 簡化計算了 !!

看下面這個問題!

給出a,b 其中 (1<=a,b<=10^6) 

求滿足條件的 x,y 的對數,使得 1<=x<=a,1<=y<=b,且gcd(x,y) == 1。

其中 (2,3) (3,2) 算兩對!

直接暴力顯然複雜度太大,我們用莫比烏斯反演來解決。

令N = max(a,b)

然後定義 F(n) 表示滿足條件的 gcd(x,y)==n的 (x,y) 對數

在定義 G(n) 表示滿足 n | gcd(x,y) 的(x,y)對數  即 gcd(x,y)%n==0 的x,y對數

那麼根據定義,有 G(n) = sigma(F(d)) (n|d,d<=N) 

於是我們需要求的就是 F(1)

怎麼解決?

首先根據G(n)的定義,可以很容易發現 G(n) = (a/n)*(b/n)這裡是向下整除 (提示:把n當成最小的元)

然後 我們只直接計算 F(1) 即可

帶入 G(n) 的公式 有  F(1) = sigma(U[i]*(a/i)*(b/i)) (1<=i<=N)

至於U[]的值,可以提前用篩法在O(n)的時間內處理出來,這樣總的時間複雜度就是 O(n),問題得到解決!!

下面附上我自己求U[]的程式碼 (效率並不是嚴格上的O(n),不過一般情況下已經足夠)

四:進階

在ACM中,可以利用 莫比烏斯反演 來求解很多關於 Gcd 的問題 

推薦幾道基礎題: SPOJ 7001 , ZOJ 3435, HDU 1695.

想做更多的題的話,自己去HUST OJ搜尋吧 :) 

最後再說一下上面的證明方法都是個人YY的,感覺比《組合數學》上的證明簡單些(數學太渣orz...那個證明我是沒看太懂),寫下來

給初學莫比烏斯反演的童鞋當個資料(= =)。關於上面的證明我暫時沒發現什麼錯誤,如果發現錯誤,請在回覆裡面指出!另外

形式二的證明應該可以由形式一直接得到,不過我沒想出什麼好辦法,知道的神牛也請在評論中說一下!

( ̄. ̄) 完~~