总共有多少个数独?
憋屈地看了一個星期的論文,實在是沒一點意思。為了娛樂一下自己,兼受同學啟發,我決定用python寫一個數獨游戲。從大二開始裝了個ubuntu系統后,就發現了這個有趣的游戲。之后每次進入這個系統,非得先來幾局數獨;再到后來,為了玩數獨,特意進了這個系統。喜歡這個自帶的游戲的特點,界面簡單,只做必要的錯誤提示,可以回溯,是我用過的最理想的數獨游戲了,呵呵。至于我自己打算做一個數獨游戲,純粹是為了現學現用。自從開始學python以來,從來沒有真正用它寫過一段帶有自己設計思想的代碼,實是不該。何不就為自己做一個windows下也能玩的稱心如意的數獨呢?好了,取名就叫Eva's Sudoku~!不過話說回來,這篇文章的主題是:[b]總共有多少個數獨[/b]?
這絕對不是一個簡單的排列組合問題。關于這個問題,《編程之美》有過一個簡單的推斷介紹。根據里面找提供的一個網址,我找到了關于這個問題的詳細解決方案[1]。現在讓我們先做一些簡單的規定:
1. 我們要求的是合法的數獨總數,定為N
2. 對于數獨中的9個塊(block),分別命名如下:
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0010/f26c1969-c57a-355a-b366-b327904a6408.jpg[/img]
[/align]
3. 對于一個塊(block),如果其內9個數字按如下方式排序,則稱其為“標準型”:
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0046/fb9a7d37-a316-3b6b-9862-4ab3ecd2ef9d.jpg[/img]
[/align]
總是可以通過替換使得一個塊成為標準型的,這個過程叫做relabeling。這樣的話,我們可以總是通過relabeling使B1成為標準型,對于一個B1是標準型的合法數獨,它將可以通過relabeling得到9!個不一樣的數獨,因此,現在我們要求解的問題變成:
[b]B1是標準型的合法數獨有多少個?[/b]設該數為N1,我們有N=N1*9!。
好了,現在B1確定下來了,為了使數獨合法,B2-B3總共有多少種可能呢?首先考慮B2的第一行,填寫它的數字要么全部來自B1的第二行或第三行,要么是B1第二、三行的混合。B2-B3第一行的所有可能如下:
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0014/99053005-87d8-3f57-96bf-49c886efcc79.jpg[/img]
[/align]
如果填寫B2第一行的數字全部來自B1的第二行或第三行(我們稱其為[b]pure的情況[/b]),合法的B2-B3的填法如下(圖為填寫B2第一行的數字全部來字B1的第二行):
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0016/72a010b4-2463-3d39-a8f0-40c59ef8e834.jpg[/img]
[/align]
每行內部都可以全排,因此總共有(3!)^6種可能,再加上填寫B2第一行的數字全部來自B1第三行的情況,對于pure的情況,總共有2*(3!)^6種可能。
如果填寫B2第一行的數字是B1第二、第三行的混合(總共有18種組合方式),列取其中一種來看(其中a,b,c各代表1,2,3中的某個數),在這種情況下,包括全排列,總共會有3*(3!)^6種排列情況:
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0018/32c5f36a-7dc2-3f18-8cb4-cb107debd13a.jpg[/img]
[/align]
因此,B2-B3的合法可能共有:
M1=2*(3!)^6+18*3*(3!)^6=56*(3!)^6=2612736
[b]為了讓問題快速得到一個接近的解,我們使用探索法來進一步分析:[/b]
我們從九宮格的每一個塊出發,如果每個塊都由1~9填充,不再有其他限制,則合法的解共有N0=(9!)^9;進一步加上限制,塊行中每一行都由1~9填充,其合法的解共有M2=9!*M1,九宮格里共有3個塊行,因此使三個塊行都合法的解共有M=M2^3,滿足塊行限制的解的個數在滿足塊限制解的個數中所占的比例為k=M/N0,同理滿足塊列限制的解的個數在滿足塊限制解的個數中所占的比例也為k。假設以上兩個比例相互獨立(事實上它們并不完全獨立),則同時滿足塊行解和塊列解在滿足塊限制解中的比較約為k^2,因此同時滿足塊行列限制的解(即九宮格的解)的總數約為N0*k^2~6.657*10^21。該估計結果與精確結果6.671*10^21相差大約0.2%。
[b]現在讓我們進入精確結果的求解過程:[/b]
總的來說,求解的思路是暴力求解。設置兩個loop循環,外循環窮舉所有在B1為標準型的情況下B2-B3所有可能的解,內循環則在B2-B3的解的情況下窮舉所有合法的數獨的解。
[b]1. 外循環[/b]
現在我們知道了B2-B3所有可能的解的個數了(M1=2612736),外循環將執行200多萬次!這一想就讓人覺得遙遙無期,有什么辦法能夠減少窮舉的次數呢?選代表~!我們總是能夠通過行列變換、交換元素等方法來使一個數獨轉變成另外一個數獨,這樣的話,當我們知道了第一個數獨的解法,我們自然而然就知道了第二個數獨的解法,第二個數獨將與第一個數獨有同樣的解法個數(不知道你明白我的意思了沒?)由此我們可能在所有B2-B3可能的解(M1=2612736種)的集合中定義等價關系,從而使等價集中的待完成的數獨具有相等個數的解。
怎么從一個數獨轉換成另一個數獨呢?前面我們使用了relabeling技術,但事實上除了這個以外,還有其他技術,如塊交換,例如我們交換B2跟B3,相應的解只需要交換B5跟B6,B8跟B9,完成B2-B3的解的個數將與完成B3-B2的解的個數相等。我們還可以對B1、B2、B3進行全排,下面的B4~B9只需做相應的順序調整。雖然這將使B1不再是標準型的,但是別忘了我們還可以通過relabeling技術使它成為標準型的。甚至我們還可以對塊的列、行進行全排。這些技術將幫助我們選舉出一些特定的代表來進入內循環,內循環算出的該代表的個數乘以該代表所在集合的個數,將是該集合的合法的數獨解的個數。我決定用數學公式來表達這些繞口的事情:
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0022/a7b16342-e8fb-3e42-810f-b3ec13381c65.png[/img]
[/align]
現在我們的目標就是減少這個外循環的次數k。上面的分析我們知道,對B2、B3內的列進行全排列得到的解屬于一個集合,對B2、B3進行交換得到的解也屬于一個集合。因此我們選取的代表具有如下特點:
1. B2,B3的第一行是增序的;
2. B2的第一行的第一個數字小于B3第一行的第一個數字。
每個代表都存在2*(3!)^2種原始序列通過轉換(lexicographical reduction)變成該代表,也就是說,解決了一個代表的解的個數,我們實際上解決了72種情況下的解的個數,現在我們把外循環k由2612736降到了36288(=2612736/72)。
上面的做法事實上并沒有完全地利用全排列與relabeling技術。對于這36288種可能,我們可以對B1-B2-B3進行全排列,也可以對每個塊中的三個列進行全排列,從而可以得到6^4=1296種不同的解,然后對B1進行relabeling,再對B2-B3進行lexicographical reduction從而使其成為一個代表,這將使原先的36288個代表進一步降到只剩下2051個代表(具體得出的結果是通過程序完成的);更進一步,可以對B1-B2-B3的行進行全排列,然后再將B1進行relabeling變換得到一個新的可能,最終,可以得到416個代表。
有時候交換特定元素并不改變數獨其他元素的解,例如下面塊行,完成他們的解的個數的相同的(因此它們也是等價的):
[img]http://dl.iteye.com/upload/attachment/0068/0028/647f4959-8b23-3c3a-8025-efb1cb2b81f7.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/0068/0030/c684883d-3be6-36a8-85d7-c5f06edce98b.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/0068/0032/c1c409c5-5e00-3545-ac62-5042bae127ed.jpg[/img]
除了列6跟列9的元素對(8,9)之外,列4、7的(1,2)、列1、4的(1,4)、列2、9的(5,8)以及列3、6的(6,9)之間的交換也能達到同樣的效果。
更近一步,除了一對一的交換,還有2對2的交換等等,如下圖:
[img]http://dl.iteye.com/upload/attachment/0068/0034/039aa95f-84a4-33d4-acc6-8e17949b47af.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/0068/0036/f249ce03-e0ec-3cfa-95f6-cbe85b4d80ec.jpg[/img]
通過消除這些等價關系,最終我們只剩下了71個代表。通過解答這71個代表,最終發現其實只有44種完全不同的代表。也就是說,最終我們可以將外循環降到k=44。
2. 內循環
內循環的工作就是針對B1-B2-B3的44種代表中的每一個,窮舉所有合法的完整數獨,然后根據我們上面提供的公式,即可求出N1進而求出N。但是我們精益求精,對B2-B3的進行選舉代表的辦法也同樣可以運用到B4、B7上,當然,由于它受到更多的限制,我們只限制B4、B7的第一列是遞增的,這樣我們可以通過行的全排列來求出其他的數獨,這樣的做法使內循環的速度提升了72倍。
通過對這44種代表的窮舉,我們最終將得到N1=18383222420692992,進一步得到N=N1*9!=6670903752021072936960 ~ 6.671*10^21。
------------------------
[1]http://www.afjarvis.staff.shef.ac.uk/sudoku/
這絕對不是一個簡單的排列組合問題。關于這個問題,《編程之美》有過一個簡單的推斷介紹。根據里面找提供的一個網址,我找到了關于這個問題的詳細解決方案[1]。現在讓我們先做一些簡單的規定:
1. 我們要求的是合法的數獨總數,定為N
2. 對于數獨中的9個塊(block),分別命名如下:
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0010/f26c1969-c57a-355a-b366-b327904a6408.jpg[/img]
[/align]
3. 對于一個塊(block),如果其內9個數字按如下方式排序,則稱其為“標準型”:
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0046/fb9a7d37-a316-3b6b-9862-4ab3ecd2ef9d.jpg[/img]
[/align]
總是可以通過替換使得一個塊成為標準型的,這個過程叫做relabeling。這樣的話,我們可以總是通過relabeling使B1成為標準型,對于一個B1是標準型的合法數獨,它將可以通過relabeling得到9!個不一樣的數獨,因此,現在我們要求解的問題變成:
[b]B1是標準型的合法數獨有多少個?[/b]設該數為N1,我們有N=N1*9!。
好了,現在B1確定下來了,為了使數獨合法,B2-B3總共有多少種可能呢?首先考慮B2的第一行,填寫它的數字要么全部來自B1的第二行或第三行,要么是B1第二、三行的混合。B2-B3第一行的所有可能如下:
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0014/99053005-87d8-3f57-96bf-49c886efcc79.jpg[/img]
[/align]
如果填寫B2第一行的數字全部來自B1的第二行或第三行(我們稱其為[b]pure的情況[/b]),合法的B2-B3的填法如下(圖為填寫B2第一行的數字全部來字B1的第二行):
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0016/72a010b4-2463-3d39-a8f0-40c59ef8e834.jpg[/img]
[/align]
每行內部都可以全排,因此總共有(3!)^6種可能,再加上填寫B2第一行的數字全部來自B1第三行的情況,對于pure的情況,總共有2*(3!)^6種可能。
如果填寫B2第一行的數字是B1第二、第三行的混合(總共有18種組合方式),列取其中一種來看(其中a,b,c各代表1,2,3中的某個數),在這種情況下,包括全排列,總共會有3*(3!)^6種排列情況:
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0018/32c5f36a-7dc2-3f18-8cb4-cb107debd13a.jpg[/img]
[/align]
因此,B2-B3的合法可能共有:
M1=2*(3!)^6+18*3*(3!)^6=56*(3!)^6=2612736
[b]為了讓問題快速得到一個接近的解,我們使用探索法來進一步分析:[/b]
我們從九宮格的每一個塊出發,如果每個塊都由1~9填充,不再有其他限制,則合法的解共有N0=(9!)^9;進一步加上限制,塊行中每一行都由1~9填充,其合法的解共有M2=9!*M1,九宮格里共有3個塊行,因此使三個塊行都合法的解共有M=M2^3,滿足塊行限制的解的個數在滿足塊限制解的個數中所占的比例為k=M/N0,同理滿足塊列限制的解的個數在滿足塊限制解的個數中所占的比例也為k。假設以上兩個比例相互獨立(事實上它們并不完全獨立),則同時滿足塊行解和塊列解在滿足塊限制解中的比較約為k^2,因此同時滿足塊行列限制的解(即九宮格的解)的總數約為N0*k^2~6.657*10^21。該估計結果與精確結果6.671*10^21相差大約0.2%。
[b]現在讓我們進入精確結果的求解過程:[/b]
總的來說,求解的思路是暴力求解。設置兩個loop循環,外循環窮舉所有在B1為標準型的情況下B2-B3所有可能的解,內循環則在B2-B3的解的情況下窮舉所有合法的數獨的解。
[b]1. 外循環[/b]
現在我們知道了B2-B3所有可能的解的個數了(M1=2612736),外循環將執行200多萬次!這一想就讓人覺得遙遙無期,有什么辦法能夠減少窮舉的次數呢?選代表~!我們總是能夠通過行列變換、交換元素等方法來使一個數獨轉變成另外一個數獨,這樣的話,當我們知道了第一個數獨的解法,我們自然而然就知道了第二個數獨的解法,第二個數獨將與第一個數獨有同樣的解法個數(不知道你明白我的意思了沒?)由此我們可能在所有B2-B3可能的解(M1=2612736種)的集合中定義等價關系,從而使等價集中的待完成的數獨具有相等個數的解。
怎么從一個數獨轉換成另一個數獨呢?前面我們使用了relabeling技術,但事實上除了這個以外,還有其他技術,如塊交換,例如我們交換B2跟B3,相應的解只需要交換B5跟B6,B8跟B9,完成B2-B3的解的個數將與完成B3-B2的解的個數相等。我們還可以對B1、B2、B3進行全排,下面的B4~B9只需做相應的順序調整。雖然這將使B1不再是標準型的,但是別忘了我們還可以通過relabeling技術使它成為標準型的。甚至我們還可以對塊的列、行進行全排。這些技術將幫助我們選舉出一些特定的代表來進入內循環,內循環算出的該代表的個數乘以該代表所在集合的個數,將是該集合的合法的數獨解的個數。我決定用數學公式來表達這些繞口的事情:
[align=center]
[img]http://dl.iteye.com/upload/attachment/0068/0022/a7b16342-e8fb-3e42-810f-b3ec13381c65.png[/img]
[/align]
現在我們的目標就是減少這個外循環的次數k。上面的分析我們知道,對B2、B3內的列進行全排列得到的解屬于一個集合,對B2、B3進行交換得到的解也屬于一個集合。因此我們選取的代表具有如下特點:
1. B2,B3的第一行是增序的;
2. B2的第一行的第一個數字小于B3第一行的第一個數字。
每個代表都存在2*(3!)^2種原始序列通過轉換(lexicographical reduction)變成該代表,也就是說,解決了一個代表的解的個數,我們實際上解決了72種情況下的解的個數,現在我們把外循環k由2612736降到了36288(=2612736/72)。
上面的做法事實上并沒有完全地利用全排列與relabeling技術。對于這36288種可能,我們可以對B1-B2-B3進行全排列,也可以對每個塊中的三個列進行全排列,從而可以得到6^4=1296種不同的解,然后對B1進行relabeling,再對B2-B3進行lexicographical reduction從而使其成為一個代表,這將使原先的36288個代表進一步降到只剩下2051個代表(具體得出的結果是通過程序完成的);更進一步,可以對B1-B2-B3的行進行全排列,然后再將B1進行relabeling變換得到一個新的可能,最終,可以得到416個代表。
有時候交換特定元素并不改變數獨其他元素的解,例如下面塊行,完成他們的解的個數的相同的(因此它們也是等價的):
[img]http://dl.iteye.com/upload/attachment/0068/0028/647f4959-8b23-3c3a-8025-efb1cb2b81f7.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/0068/0030/c684883d-3be6-36a8-85d7-c5f06edce98b.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/0068/0032/c1c409c5-5e00-3545-ac62-5042bae127ed.jpg[/img]
除了列6跟列9的元素對(8,9)之外,列4、7的(1,2)、列1、4的(1,4)、列2、9的(5,8)以及列3、6的(6,9)之間的交換也能達到同樣的效果。
更近一步,除了一對一的交換,還有2對2的交換等等,如下圖:
[img]http://dl.iteye.com/upload/attachment/0068/0034/039aa95f-84a4-33d4-acc6-8e17949b47af.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/0068/0036/f249ce03-e0ec-3cfa-95f6-cbe85b4d80ec.jpg[/img]
通過消除這些等價關系,最終我們只剩下了71個代表。通過解答這71個代表,最終發現其實只有44種完全不同的代表。也就是說,最終我們可以將外循環降到k=44。
2. 內循環
內循環的工作就是針對B1-B2-B3的44種代表中的每一個,窮舉所有合法的完整數獨,然后根據我們上面提供的公式,即可求出N1進而求出N。但是我們精益求精,對B2-B3的進行選舉代表的辦法也同樣可以運用到B4、B7上,當然,由于它受到更多的限制,我們只限制B4、B7的第一列是遞增的,這樣我們可以通過行的全排列來求出其他的數獨,這樣的做法使內循環的速度提升了72倍。
通過對這44種代表的窮舉,我們最終將得到N1=18383222420692992,進一步得到N=N1*9!=6670903752021072936960 ~ 6.671*10^21。
------------------------
[1]http://www.afjarvis.staff.shef.ac.uk/sudoku/
總結
- 上一篇: SAP第三代增强——BADI解读
- 下一篇: 《七堂极简物理课》总结