2015年5月10日日曜日

Rで順列の生成

ins=function(v,o)sapply(length(v):0,append,x=v,value=o)
perm=function(n){
  if(n==1) as.matrix(1)
  else matrix( apply(perm(n-1),2,ins,o=n) , nrow=n )
}


> perm(3)
      [,1] [,2] [,3] [,4] [,5] [,6]
 [1,]    1    1    3    2    2    3
 [2,]    2    3    1    1    3    2
 [3,]    3    2    2    3    1    1


Rの勉強のために順列の生成に挑戦した。悪戦苦闘の末、上のようなコードを編み出した。
サンプルとして表示しているのは1・2・3からなるすべての順列で、各列が1つ1つの順列をあらわす行列である。

perm(n)で1:nからなる順列を生成する。
さて、コードを解説しよう。
再帰的に実行するが、その着想はこうである。
1:nのすべての順列があったとして、1:(n+1)の全順列はどのように生成したらよいか。
1:nの順列のうちの1つを ○○○○ としよう。そこに(n+1)を付け加えるわけだが、 ○○○○ の一番前から一番後ろまでの(n+1)通りの置き方がある。具体的に示せば
 (n+1)○○○○  ○(n+1)○○○  ○○(n+1)○○  ○○○(n+1)○  ○○○○(n+1)
である。これを1:nのすべての順列に対して施せばよい。
最初の関数 ins(v,o) はベクトルvの一番前から一番後ろまでの length(v)+1 箇所に o を置いたものを各列とする行列を返す。
> ins(c(1,4,6),8)
     [,1] [,2] [,3] [,4]
[1,]    1    1    1    8
[2,]    4    4    8    1
[3,]    6    8    4    4
[4,]    8    6    6    6
1,4,6という順列に8を付け加えた。
appendはappend(x,values,after)という形でベクトルxの位置afterの後ろにvaluesを挿入する。
perm(n)は再帰的に定義される。
perm(1)は1だけからなる順列だから1だけである。再帰的に使うときに行列として認識されないといけないようなので、as.matrixで行列としている。
perm(n-1)が1:(n-1)からなる全順列を、列もちで持っている行列とすると、そこにnを付け加えるには、その各列(すなわち順列)vに対して、ins(v,n)を施せばよい。それが apply(perm(n-1),2,ins,o=n) である。これがn行n!(nの階乗)列の行列であればよいのだが、そうはうまくいかない。縦に重なっていく。で、仕方がないので、matrixをかぶせて、行数をnに強制している。

0 件のコメント: