莫比烏斯反演入門(詳解)
最近在學習莫比烏斯反演的知識,看了很多網上的部落格,大多都是堆了很多公式但是講得不夠詳細,不太容易理解,這裡推薦一個寫得很詳細的部落格:
這個文章主要講一下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...那個證明我是沒看太懂),寫下來
給初學莫比烏斯反演的童鞋當個資料(= =)。關於上面的證明我暫時沒發現什麼錯誤,如果發現錯誤,請在回覆裡面指出!另外
形式二的證明應該可以由形式一直接得到,不過我沒想出什麼好辦法,知道的神牛也請在評論中說一下!
( ̄. ̄) 完~~