1. 程式人生 > >[原創]關於a1,a2,a3,...,an共n個元素依次入棧其可能出棧的排列數的計算

[原創]關於a1,a2,a3,...,an共n個元素依次入棧其可能出棧的排列數的計算

以前一直以為1,2,3,4依次入棧可能出棧的順序只有一種,那就是4321,因為一直認為棧是先進後出的,所以.....,最後終於知道事實上在後面的元素入棧之前前面的元素可能會出棧,比如在4入棧之前321就已經依次出棧了,那出棧序列則為3214;那麼當每一個元素都滿足剛入棧就立即出棧時則出棧序列為1234,這個序列是我當時最想不通的了,因為一直信奉“先進後出”,呵呵。以前對這部分也是很迷糊的,前段時間在編寫二叉樹的非遞迴的先序遍歷演算法時,終於意識到出棧次序的重要性,所以後來好好研究,終於解決了這個問題的一大半

先說下解決這個問題的主要方法:分冶法+遞迴

現考慮a1,a2,a3,...,an共n個元素依次入棧(a1,a2,a3,...,an這n個元素不一定是有序的,或者說這n個元素可以是任意順序,但是應該必須保證每個元素是唯一的):

設用S(i,n)表示“a1,a2,a3,...,ai,...,an這n個元素中第i個元素ai最先出棧時所有的排列數”,那麼當ai出棧後,緊接著ai出棧後的出棧元素必然是{ a(i-1), a(i+1), a(i+2), ... ,a( i + n-i ) }這(n-i+1)個元素中的某一個元素,現在先假設i>1以使a(i-1)有意義——這是因為這n個元素是依次入棧的,所以當ai最先出棧時,必然ai前面的任何一個元素(指的是先於ai入棧的元素)已經入棧並且未曾出棧(因為ai最先出棧),那麼緊接著ai出棧的必然是ai後面的元素中的一個(因為後面的元素後入棧,所以可以是任意的元素緊接著ai出棧)或者a(i-1),因為對於前(i-1)個已經入棧但卻還未出棧的元素中,必須等到a(i-1)出棧後,a(i-2),a(i-3),...,a1這些元素才有機會出棧,說得土的一點就是,a(i-1)堵住了前面的(i-2)個元素!

1、對於a(i-1)緊接著ai出棧時:a(i-1)是{ a1,a2,a3, ..., a(i-1) , a(i+1), a(i+2) , ... , an }共(n-1)個元素第i-1個元素,所以這種情況下所有的出棧的排列數為S( i-1, n-1 )

2、對於a(i+1)緊接著ai出棧時:a(i+1)是{ a1,a2,a3, ..., a(i-1) , a(i+1), a(i+2) , ... , an }共(n-1)個元素第i個元素(注意哦),所以這種情況下所有的出棧的排列數為S( i, n-1 )

3、對於a(i+2)緊接著ai出棧時:a(i+2)是{ a1,a2,a3, ..., a(i-1) , a(i+1), a(i+2) , ... , an }共(n-1)個元素第(i+1)個元素(注意哦

),所以這種情況下所有的出棧的排列數為S( i+1, n-1 )

.

.

.

(n-i+1)、對於a( i + n-i )[即an]緊接著ai出棧時:an是{ a1,a2,a3, ..., a(i-1) , a(i+1), a(i+2) , ... , an }共(n-1)個元素第(n-1)個元素(注意哦),所以這種情況下所有的出棧的排列數為S( n-1, n-1 )

那麼根據加法原理,得到:

S(i,n) = S( i-1, n-1 ) + S( i, n-1 ) + S( i+1, n-1 ) + S( i+2, n-1 ) + ... + S( i+ n-1-i , n-1 )

這裡共有(n-i+1)個數相加

顯然當i=1時,前面不可能有元素會緊接著a1出棧(說過有a0這個元素嗎?),所以為了i=1時滿足上式,規定而且事實上也應該是S( 0, n ) = 0 ,那麼當i=n時,顯然S( n, n ) = 1 ,因為第n個元素(最後入棧的元素)最先出棧時其他元素都是被前面一個元素堵住了,所以只有一種排列。

對於上面的公式:當取i的值為(i+1),則有

S(i+1, n) =  S( i, n-1 ) + S( i+1, n-1 ) + S( i+2, n-1 ) + ... + S( i+ n-1-i , n-1 )

這裡共有( n -(i+1) +1) 個數相加

二式相減得到:

S( i,n ) - S( i+1, n ) = S( i-1, n-1 ) 即 S(i,n) = S(i-1, n-1) + S(i+1, n )

另外S( 0, n ) = 0 且 S( n, n ) = 1

所以這樣就能算出S( i,n )

然後是a1,a2,a3, ..., an共n個元素依次入棧後所有的可能出棧的排列數為T(n) , 則

T(n) = S( 1, n ) + S( 2, n ) + S( 3, n ) + ... + S( n-1, n ) + S( n, n )

最終應該求出T(n) , 很遺憾的是在高中時我沒能好好學習排列組合知識,所以目前無法用現有的數學運算子來表達出T(n)或S(i,n),或者說我求不出來T(n)或S(i,n),如果有人能夠算出來,請通知我一下!

所以我只得編寫程式求解這個問題了,因為計算機的計算能力很強!

執行結果如下:

1
T(1)=1  1!=1    T(1)/1!=1

1 1
T(2)=2  2!=2    T(2)/2!=1

2 2 1
T(3)=5  3!=6    T(3)/3!=0.833333

5 5 3 1
T(4)=14 4!=24   T(4)/4!=0.583333

14 14 9 4 1
T(5)=42 5!=120  T(5)/5!=0.35

42 42 28 14 5 1
T(6)=132        6!=720  T(6)/6!=0.183333

132 132 90 48 20 6 1
T(7)=429        7!=5040 T(7)/7!=0.085119

429 429 297 165 75 27 7 1
T(8)=1430       8!=40320        T(8)/8!=0.0354663

1430 1430 1001 572 275 110 35 8 1
T(9)=4862       9!=362880       T(9)/9!=0.0133984

4862 4862 3432 2002 1001 429 154 44 9 1
T(10)=16796     10!=3628800     T(10)/10!=0.00462853

16796 16796 11934 7072 3640 1638 637 208 54 10 1
T(11)=58786     11!=39916800    T(11)/11!=0.00147271

58786 58786 41990 25194 13260 6188 2548 910 273 65 11 1
T(12)=208012    12!=479001600   T(12)/12!=0.000434262

208012 208012 149226 90440 48450 23256 9996 3808 1260 350 77 12 1
T(13)=742900    13!=1932053504  T(13)/13!=0.000384513

本來用截圖做結果,但是可惜不能上傳圖片,這個程式在gcc和VS2008裡面測試通過,關於遞迴的能否結束,其實只要你注意一下每次遞迴呼叫後i的變化就能知道遞迴是否會最終結束,因為0<= i <=n,注意到這一點就可以很容易瞭解遞迴最終會結束,但是我還是不期望以不合法的資料呼叫函式,在呼叫函式時請確保0<= i <=n ,由執行結果可以看出T(n)的變化遠遠沒有n!的變化快,因為T(n)只不過是累加的過程,所以儘管S(i,n)被呼叫多次,但由於每次計算都是加,所以計算還是很快的(0.133s)。注意理解S(i,n)表示“a1,a2,a3,...,ai,...,an這n個元素中第i個元素ai最先出棧時所有的排列數”,這是求解問題和運用遞迴的關鍵!現在解決這個問題算是有頭緒了,準備再寫程式輸出所有的可能的出棧序列。