字典序法生成全排列算法的證明


引言 對一個給定數據進行全排列,在各種場合經常會用到。組合數學中,生成全排列的方法有很多,盧開澄老師的《組合數學》中就介紹了三種:序數法,字典序法,臨位互換法等。其中以字典序法由於算法簡單,並且使用的時候可以依照當前狀態獲取下一個狀態,直到所有排列全部完成,方便在程序中隨要隨用,應用比較廣泛,STL中的Next_permutation也是使用此法。 算法定義 首先看什么叫字典序,顧名思義就是按照字典的順序(a-z, 1-9)。以字典序為基礎,我們可以得出任意兩個數字串的大小。比如 "1" < "12"<"13"。 就是按每個數字位逐個比較的結果。對於一個數字串,“123456789”, 可以知道最小的串是 從小到大的有序串“123456789”,而最大的串是從大到小的有序串“*987654321”。這樣對於“123456789”的所有排列,將他們排序,即可以得到按照字典序排序的所有排列的有序集合。 如此,當我們知道當前的排列時,要獲取下一個排列時,就可以范圍有序集合中的下一個數(恰好比他大的)。比如,當前的排列時“123456879”, 那么恰好比他大的下一個排列就是“123456897”。 當當前的排列時最大的時候,說明所有的排列都找完了。 於是可以有下面計算下一個排列的算法: 設P是1~n的一個全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn   1)從排列的右端開始,找出第一個比右邊數字小的數字的序號j(j從左端開始計算),即 j=max{i|pi<pi+1}   2)在pj的右邊的數字中,找出所有比pj大的數中最小的數字pk,即 k=max{i|pi>pj}(右邊的數從右至左是遞增的,因此k是所有大於pj的數字中序號最大者)   3)對換pi,pk   4)再將pj+1......pk-1pkpk+1......pn倒轉得到排列p'=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,這就是排列p的下一個排列。
證明
要證明這個算法的正確性,我們只要證明生成的下一個排序是恰好比當前排列大的一個序列即可。圖1.11是從盧開澄老師的《組合數學》中截取的一個有1234生成所有排序的字典序樹。從左到右的每一個根到葉子幾點的路徑就是一個排列。下面我們將以這個圖為基礎,來證明上面算法的正確性。
 
算法步驟1,得到的子串 s = {pj+1,.....,pn}, 是按照從大到小進行排列的。即有 pj+1 > pj+2 > ... > pn, 因為 j=max{i|pi<pi+1}。
算法步驟2,得到了最小的比pj大的pk,從n往j數,第一個比j大的數字。將pk和pj替換,保證了替換后的數字比當前的數字要大。
於是得到的序列為p1p2...pj-1pkpj+1...pk-1pjpk-1...pn.注意這里已經將pk替換成了pk。
這時候我們注意到比p1..pj-1pk.....,恰好比p1....pj.....pn大的數字集合。我們在這個集合中挑選出最小的一個即時所要求的下一個排列。
算法步驟3,即是將pk后面的數字逆轉一下(從從大到小,變成了從小到大。)
 
由此經過上面3個步驟得到的下個排列時恰好比當前排列大的排列。
同時我們注意到,當所有排列都找完時,此時數字串從大到小排列。步驟1得到的j = 0,算法結束。
 

算法實現

  1. /** java語言實現
  2.  * get the next permutation based on dictionary order method 
  3.  *  
  4.  * @param cur 
  5.  * @return next permutation string, or null if cur is the last 
  6.  */  
  7. public static String next(String cur) {  
  8.     String ret = null;  
  9.     if (cur == null)  
  10.         return ret;  
  11.     int strlen = cur.length();  
  12.     char[] lcur = cur.toLowerCase().toCharArray();  
  13.     int j = strlen - 2;  
  14.     while (j >= 0 && lcur[j] > lcur[j + 1]) {  
  15.         j--;  
  16.     }  
  17.     if (j < 0)  
  18.         return ret;  
  19.   
  20.     int k = strlen - 1;  
  21.     while (lcur[k] < lcur[j]) {  
  22.         k--;  
  23.     }  
  24.   
  25.     // swap lcur[k], lcur[j]  
  26.     char temp = lcur[k];  
  27.     lcur[k] = lcur[j];  
  28.     lcur[j] = temp;  
  29.   
  30.     // reverse lcur[j+1, ..., strlen-1]  
  31.     int low = j + 1;  
  32.     int high = strlen - 1;  
  33.     while (low < high) {  
  34.         temp = lcur[low];  
  35.         lcur[low] = lcur[high];  
  36.         lcur[high] = temp;  
  37.         low++;  
  38.         high--;  
  39.     }  
  40.     ret = String.valueOf(lcur);  
  41.     return ret;  
  42. }  
  43. 轉載自:http://blog.csdn.net/cpfeed/article/details/7376132

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM