2010年6月20日日曜日

順列生成

順列を生成.

再帰による辞書式順でない順列生成
public void generate(int[] a){
recursive(a,0);
}

private void recursive(int[] a,int j){
if(j==a.length){
System.out.println(Arrays.toString(a));
return;
}
for(int i=j;i<a.length;i++){
// a[i]とa[j]を入れ替え
int t=a[j]; a[j]=a[i]; a[i]=t;
recursive(a,j+1);
// a[i]とa[j]を入れ替え
t=a[j]; a[j]=a[i]; a[i]=t;
}
}

再帰による辞書式順順列生成

public void generate(int[] a){
recursive(a,0);
}

private void recursive(int[] a,int j){
if(j==a.length){
System.out.println(Arrays.toString(a));
return;
}
for(int i=j;i<a.length;i++){
// j~iを循環右ローテート
int t=a[i];
for(int k=i;k>j;k--)
a[k]=a[k-1];
a[j]=t;
recursive(a,j+1);
// j~iを循環左ローテート
for(int k=j;k<i;k++)
a[k]=a[k+1];
a[i]=t;
}
}

辞書式順でない順列生成では,単純に2つの要素を交換していますが,辞書式順の順列生成では,その操作を右ローテートに置き換えています.
右ローテートだと辞書式順になる理由は以下の通り.

a1a2…an
と要素が並んでいて,
ai>ai+1
とします.(色々説明が面倒なので要素aiを数とします)
aiを先頭に置いた時,辞書式順で初めに来る列(=最小値)は何かというと,
a1…ai-1aiai+1…an
のaiを先頭に持ってきた
ai[a1…ai-1ai+1…an]
になります.ちょうどこれは,元の列のa1…aiを右にローテートしたものです.
というわけで,i=1~nについてこの操作を実行すると,
a1[a2a3…an-1an]
a2[a1a3…an-1an]

an-1[a1a2…an-2an]
an[a1a2…an-2an-1]
とn個の列が出来ます.
それぞれの列について再帰させ,[]内でも同様の操作を行うと,1つの列あたりn-1個の列が出来ます.

a1[a2[a3a4…an-1an]]
a1[a3[a2a4…an-1an]]

a1[an-1[a2a3…an-2an]]
a1[an[a2a3…an-2an-1]]

[[]]の一番内側の列は,2項目までを決定した時の最小値です.
というわけで,n回再帰することで,辞書式順の順列が作れます.

0 件のコメント: