【原創】實現全排列

 

(轉載請標註作者姓名,也不要直接用我的文章發paper)

http://student.csdn.net/space.php?uid=32341&do=blog&id=3705

 
相關信息見這個帖子。不過付兄使用的是庫函數。我懶得去看庫函數了。今天放我的兩個方法,第二個方法Java編程實現。
題目是輸入數字n實現1到n的全排列 比如n=2則輸出 12   21
方法0,見《組合數學》老外的教材
用遞推法。第1層 1
第二層    (1空)1(2空)
空裏面就是“二”的位置,
在空裏面輸入數字2
第三層(1空)1(2空)2(3空)                             (1空)2(2空)1(3空)
空裏面就是“三”的位置以此類推
方法1,使用循環移位
比如123456循環左移1位就是  234561  再左移兩位就是 456123
這個方法可以只在頭處加上第n個數。一樣是遞推的方法,從第1層開始一直到第n層。
我懶。上面兩個方法都沒有編程實現。現在說第三個方法
數學方法:
理論:線性代數裏面有個關於逆序數的說明。說了一下沒有了。可能是我用的教材版本太低了吧。
初始行列式每行都是1到n每列都一樣。比如6吧
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
接下來展開行列式。不過不是行列式運算而是新的運算規則。從上往下開始按列展開比如
a11和A11(a11的代數餘子式)
1和
23456
23456
23456
23456
23456
a21和A21比如
2和
13456
13456
13456
13456
13456
接下來每一項都要運算。那麼ai1和Ai1一共有6個要運算的。運算方法很簡單就是放到那裏比如第一個'1'(A11)  // '1'表示數字
對代數餘子式繼續按照行列式展開的方法從上到下依次展開,運算就OK了。運算和結果放到那裏輸出。
下面給出數學證明。
假設是n0個數
1.......n0
1.......n0
.......
1.......n0
第一次有n0個數參與運算。所以第一次每個數字都運算了。
然後是代數餘子式。這個定義是去掉aij所在的行(往下運算了),去掉所在的列(以後這個數都遇不上了)
繼續算。所以每個數字都參與了運算。這個不包含正副號,都是正的,畢竟不是行列式。
再舉個例子加深影響。n=3
 
行列式
123
123
123
a11是1                        a12是2          a13是3
A11是                          A12是            A13是
23                                13                   12
23                                13                   12
就是‘1’A11         '2'A12     '3'A13三項
繼續
23
23展開就是   '2'和新A12 '3'和新A13 
結果是23  和32   和‘1’運算這個結果就是123   132
剩下的一樣。數學方法完畢。
這個編程太麻煩了代數餘子式咋表示?都是二維數組我很懶。估計要用n*n*n的空間。中間結果都要記錄下來,還要標記和調試。複雜。數學方法捨棄。
觀察行列式
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
選取一個數比如3
      3
1 2   4 5 6
1 2   4 5 6
1 2   4 5 6
1 2   4 5 6
接下來運算,最開始的想法是從3開始要麼朝左運動,要麼朝右(選數)。每個節點只訪問一遍。有沒有想起二叉樹輸出先序中序和後序的代碼。這個也複雜。要麼線索化要麼用棧。頭疼。
直接使用廣度優先搜索。仿照那個最小生成樹的兩個算法和自己改進的深度優先算法(未公開)。直接一個已運算數組,一個未運算數組保存每個值。代碼如下:

http://student.csdn.net/space.php?uid=32341&do=blog&id=3705

相關信息見這個帖子。不過付兄使用的是庫函數。我懶得去看庫函數了。今天先放方法。

題目是輸入數字n實現1到n的全排列  比如n=2則輸出 12   21

方法0,見《組合數學》老外的教材

用遞推法。第1層 1

第二層    (1空)1(2空)

空裏面就是“二”的位置,

在空裏面輸入數字2

第三層(1空)1(2空)2(3空)                             (1空)2(2空)1(3空)

空裏面就是“三”的位置以此類推

方法1,使用循環移位

比如123456循環左移1位就是  234561  再左移兩位就是 456123

這個方法可以只在頭處加上第n個數。一樣是遞推的方法,從第1層開始一直到第n層。

我懶。上面兩個方法都沒有編程實現。現在說第三個方法

數學方法:

理論:線性代數裏面有個關於逆序數的說明。說了一下沒有了。可能是我用的教材版本太低了吧。

初始行列式每行都是1到n每列都一樣。比如6吧

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

接下來展開行列式。不過不是行列式運算而是新的運算規則。從上往下開始按列展開比如

a11和A11(a11的代數餘子式)

1和

23456

23456

23456

23456

23456

a21和A21比如

2和

13456

13456

13456

13456

13456

接下來每一項都要運算。那麼ai1和Ai1一共有6個要運算的。運算方法很簡單就是放到那裏比如第一個'1'(A11)  // '1'表示數字

對代數餘子式繼續按照行列式展開的方法從上到下依次展開,運算就OK了。運算和結果放到那裏輸出。

 

下面給出數學證明。

假設是n0個數

1.......n0

1.......n0

.......

1.......n0

第一次有n0個數參與運算。所以第一次每個數字都運算了。

然後是代數餘子式。這個定義是去掉aij所在的行(往下運算了),去掉所在的列(以後這個數都遇不上了)

繼續算。所以每個數字都參與了運算。這個不包含正副號,都是正的,畢竟不是行列式。

再舉個例子加深影響。n=3

 

行列式

123

123

123

a11是1                        a12是2          a13是3

A11是                          A12是            A13是

23                                13                   12

23                                13                   12

就是‘1’A11         '2'A12     '3'A13三項

繼續

23

23展開就是   '2'和A12 '3'和A12 

結果是23  和32   和‘1’運算這個結果就是123   132

剩下的一樣。數學方法完畢。

這個編程太麻煩了代數餘子式咋表示?都是二維數組我很懶。估計要用n*n*n的空間。中間結果都要記錄下來,還要標記和調試。複雜。數學方法捨棄。

觀察行列式

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

選取一個數比如3

      3

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

Code:
  1. public class quanpailie {   
  2.   
  3.     /**  
  4.      * @param args  
  5.      */  
  6.     //全排列函數,支持數字輸入和字符 輸入   
  7. //這個是字符串的前面是使用過的字母串,後面是未使用的。   
  8.     public  void quanpailie(String usingstr,String unusestr,int n){   
  9.   
  10.         char a[]=unusestr.toCharArray();   
  11.         //終止部分   
  12. //遞歸結束的部分   
  13.         if(usingstr.length()==n-1)   
  14.         {   
  15.             usingstr+=a[0];   
  16.         //輸出結果,採用打印的方式   
  17.         //System.out.print(usingstr+"/t");   
  18.         }   
  19.            
  20.   
  21.         //遞歸部分   
  22.         for(int i=0;i<a.length;i++){   
  23.             String newusingstr=usingstr+a[i];   
  24.             String newunusestr="";   
  25.             for(int j=0;j<a.length;j++)   
  26.             {   
  27.                 if(i==j)continue;   
  28.                 newunusestr+=a[j];   
  29.             }   
  30.                
  31.             quanpailie(newusingstr,newunusestr,n);   
  32.         }   
  33.     }   
  34.        
  35. /*好吧我承認剛纔使用了字符串的庫函數。這個是隻支持數組的。稍微改一下在C語言中也能運行。參數分別是數組和數組長度。數組分別是使用的和未使用的,最後一個是總長。其實可以不要的- -b  */  
  36.     public void fastquanpailie(char using[],int uslength,char unuse[],int unuslength,int n){   
  37. //已使用的字符串長度加1   
  38.         char newusing[] =new char[uslength+1];   
  39.            
  40.         for(int i=0;i<uslength;i++)   
  41.             newusing[i]=using[i];//新數組中寫入原數組的值   
  42.         if(n-1==uslength){   
  43.             newusing[uslength]=unuse[0];//最後位置寫入最後一個字母   
  44.                
  45.             //輸出結果   
  46.             for(int i=0;i<newusing.length;i++)   
  47.                 System.out.print(newusing[i]);   
  48.                
  49.             System.out.print("/t");//爲了好看   
  50.         }   
  51.         //遞歸部分   
  52.         for(int i=0;i<unuslength;i++){   
  53.             //初始化   
  54.             char newunuse[] =new char[unuslength-1];   
  55.             newusing[uslength]=unuse[i];//新一位寫入新數組最後   
  56.             for(int j=0,k=0;j<newunuse.length;)//處理老數組   
  57.             {    
  58.                 if(i==j){k++;}   
  59.                 newunuse[j]=unuse[k];   
  60.                 k++;j++;   
  61.             }   
  62.             fastquanpailie(newusing,uslength+1,newunuse,unuslength-1,n);   
  63.         }   
  64.     }   
  65.        
  66.     //輔助輸入的方法。把原始輸入轉化成函數可以識別的輸入   
  67.     public void inputmode(String str){   
  68.         char a[]=str.toCharArray();   
  69.         int strlength=a.length;   
  70.         quanpailie("",str,strlength);   
  71.     }   
  72.     public void fastinputmode(char array[],int length){   
  73.   
  74.            
  75.         fastquanpailie(null,0,array,length,length);   
  76.     }   
  77.     //數字輸入輔助   
  78.     public  void Noinputs(int n){   
  79.         String str="";   
  80.         for(int i=1;i<=n;i++){   
  81.             str+=i;   
  82.         }   
  83.         inputmode(str);   
  84.     }   
  85.     //字母輸入輔助,比如輸入1輸出a輸入2輸出ab   
  86.     public  void letterinputs(int n){   
  87.         char letterarray[]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n',   
  88.                 'o','p','q','r','s','t','u','v','w','x','y','z'};   
  89.         String str="";   
  90.         for(int i=0;i<n;i++){   
  91.             str+=letterarray[i];   
  92.         }   
  93.         inputmode(str);   
  94.     }   
  95.     public  void letterinputs1(int n){   
  96.         char letterarray[]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n',   
  97.                 'o','p','q','r','s','t','u','v','w','x','y','z'};   
  98.   
  99.         fastinputmode(letterarray,n);   
  100.     }   
  101.     public static void main(String[] args) {   
  102.   
  103.         System.out.println("這個是字母輸入");   
  104.         long starttime=System.currentTimeMillis();   
  105.            
  106.         quanpailie a=new quanpailie();   
  107.         //a.letterinputs(12);   
  108.         a.letterinputs1(6);   
  109.         long endtime=System.currentTimeMillis();   
  110.         System.out.println();   
  111.         System.out.println( "用時"+(endtime-starttime));   
  112.     }   
  113.   
  114. }  

最後還是使用了庫函數System.out.print()這個不讓用你就看不到結果了。一共有兩個方法。分別是數字輸出和字母輸出。數字的一位就是一個數字。1到9都沒問題。10怎麼辦?用字母輸出。a對應1z對應26.這樣n小於等於26都沒問題。

程序複雜度N*N 空間複雜度N*N而且是遞歸函數。所以實際上n=8都是一秒以內。到了9就很慢了。到了10 編譯器eclipse無法響應。在cmd下編譯運行都沒問題。可以到12.到14的時候我沒耐心了。實際上調用System.gc()好像速度更慢了。不知道以前的對象生命週期是咋結束的。

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