如何遍歷組合數?(不是求組合數,含代碼)

1、遍歷組合數的方法原理

組合數是高中代數的知識,這裏我就不廢話了。

這裏討論的是如何遍歷組合數。舉個例子,一個電影院裏面,有十個座位和8個客人,到底有多少種坐法?(不考慮客人之間的相互位置。)高中組合數的知識告訴我們答案是C = 10 * 9 / 1 * 2 = 45種。

多少種坐法現在我們知道了,現在我們來考慮另外一個變態一點的問題,能不能把45種坐法都羅列出來。

這裏給每個座位都編個號(假設第一個座位編號爲0),這樣就可以列舉一些可能的坐法出來:

0 1  2  3  4  5  6  7  8

0 1  2  3  4  5  6  7  9

0 1  2  3  4  5  6  8  9

0 1  2  3  4  5  7  8  9

0 1  2  3  4  6  7  8  9

............

上面的例子來說,對於一般人來說可能還不是很困難,反正也就是45個而已嘛,一個個數總可以數出來。但如果改變題法,比如,50個座位坐5個客人,這樣的組合數就達到百萬級了,這樣的任務對於人來說就已經很困難。

因爲組合數的增長速度近似於是指數級的增長,所以靠人肉眼去數是絕對數不過來的,那靠計算機能否實現呢?當然能。不過這需要用到一些數學上面的小技巧。下面的定理給出了這種遍歷方法的數學原理。

定理1 設由n個正整數組成的整數集S裏,存在着一個固定長度l的有序數列A {A | a{_{n}} \epsilon S, 0 < n <= l, a{_{n-1}} < a{_{n}}},則由所有滿足這種關係的序列所組成的序列集{A' | a_{n} \epsilon A, A \epsilon A'  },就是S關於l的所有組合。

證明:證明該定理,核心是證明是否所有可能的組合情況都能通過序列A的形式表現出來。

(反證法)假設存在着一種整數組合B不滿足A的條件,現設b{_{k}} > b{_{k + 1}},互換這兩個數的位置,仍可以使第k位和第k+1位滿足約束關係。但互換這兩個位置後,原來的次序大小關係可能會被打破。但因爲組成B的數都是正整數,根據自然數的良序關係,通過在B內進行位置的互換,總能找到一個子序列B'使b{_{k}} < b{_{q}}, k < q < l。因此通過對B內的數字進行位置互換,總有一個辦法使B滿足A的約束條件。這與假設矛盾,命題成立。

簡而言之,就是在整數集內任意取l個值,總能取出一個按序遞增(減)的數列。這聽起來像廢話,但結論卻很重要。這給出了一個在工程上遍歷所有組合情況的思路。

2、算法步驟

  1. 以由小(大)到大(小)的順序按最小步長依次初始化數組的各個元素。其中數組的第一個元素爲數集的最小(大)元素。
  2. 從數組最後一位開始加(減)最小步長
  3. 當最後一位遇到溢出(溢出的閥值由整數集的大小確定)時,向前(向後)進位。
  4. 若前(後)一位也繼續溢出時,繼續向前(後)進位,直至不再溢出,或已經溢出到最前一位,算法結束。
  5. 輸出數組的值,然後繼續執行2直至算法結束。

下面證明該算法能遍歷所有可能的整數組合。

證明:如果將數組看成是一個n進制數,那麼數組的每一位元素就相當於各個數位上的值。因爲初始化的時候是以從小到大的順序排列,且最高位爲數集中最小的數字S0。所以初始化時構造出的數值(假設爲Xmin)必定是最小的。現在按最小步長增加數組的數值,在數組長度有限和可取數字有限的情況下,必定能取遍(Xmin,Xmax)所有的數字組合。

現假設某個數字組合B不能透過上述算法取得,那麼這個數字組合對應的n進制數Vb要麼大於Xmax,要麼小於Xmin。

先考慮Vb < Xmin的情況,Vb < Xmin,意味着在Vb某個高位上存在着一個整數值v 小於Xmin對應位置的值。我們對數值上的所有位從高到低來進行討論(因爲如果低位數值大,但高位數值小,實際的數值也是比原來小)由於最高位數值已經是最小值,不能再小,所以,只能選擇次高位。但次高位除非取最小值S0,否則,無法達到比Xmin小的效果。但一旦取S0,組合B就不再是組合數,與原來的題設矛盾。同樣的討論可以繼續進行,對後面各位的討論會發現,除非取前面相同的數字,否則無法找到另外一個數能使Vb < Xmin。因此Vb 一定不小於Xmin.

再來討論Vb > Xmax的情況,由於討論最大值的情況也是相似,可以發現每一位,除非取後面相同的值,否則沒法達到Vb > Vmax的效果。

由此,可以明確地知道該算法的確能遍歷所有組合數。

3、遍歷算法的意義

由於組合數的增長是很可觀的,即便計算機硬件技術發展到今天,恐怕也沒有哪個程序會去求解所有排列組合情況。遍歷算法的意義在於,可以在不需要考慮所有排列組合的情況下,挑出心目中最滿意的組合。

還是舉上面那個例子,假設每個客人對座位的要求都是挑剔的,如果你是電影院的老闆,如果隨意給他們編座位,可能馬上就收到差評。但是也不可能羅列所有的情況讓他們自己去挑。因爲50個座位給5個人去挑就已經有差不多幾百萬種情況。因此可能的方案,是隨機挑其中的幾種給他們去選。但隨機取出來的東西,又不能重複,否則程序就會出現死循環。因此最好的方案,就是把每種情況變成一個數值,通過數值的變化,來實現方案的永不重複。(當然現實生活中,最好的方案,就是由客人自己去挑自己心儀的座位,而不是編好位讓他們去選。)

總括而言,遍歷算法的意義,在於對一些涉及到排列組合的問題上,能快速尋找到下一個組合情況,並能通過存儲組合的數字值來代替存儲組合本身,節省計算機資源。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章