我想输出ATCG四个字符,组成一个12个字符长度字符串的全排列。共4^12=16777216种排列,
AAAAAAAAAAAA
AAAAAAAAAAAT
AAAAAAAAAAAC
AAAAAAAAAAAG
AAAAAAAAAATA
`````````````````````````
按照正常的想法是通过多重循环来生成全排列,所以我写下了下面这样的代码,但是有个问题,它不支持多扩展性,如果100个全排列,那么得写一百次循环嵌套。
所以我求助了perl-china群里的高手,其中一个给了一个结合二进制的巧妙解决方案。
perl -le '{$a{"00"}="A";$a{"01"}="T";$a{"10"}="C";$a{"11"}="G";for(0..100){print join"",map{$a{$_}} unpack("a2"x12,sprintf("%024b\n",$_))}}'
AAAAAAAAAAAA
AAAAAAAAAAAT
AAAAAAAAAAAC
AAAAAAAAAAAG
AAAAAAAAAATA
AAAAAAAAAATT
AAAAAAAAAATC
AAAAAAAAAATG
AAAAAAAAAACA
AAAAAAAAAACT
AAAAAAAAAACC
AAAAAAAAAACG
AAAAAAAAAAGA
AAAAAAAAAAGT
AAAAAAAAAAGC
AAAAAAAAAAGG
AAAAAAAAATAA
AAAAAAAAATAT
这里我简单解释一下这个代码
perl -le '{$a=sprintf("%024b\n",1);print $a}'
000000000000000000000001
这个sprintf是根据024b的格式把我们的十进制数字转为二级制,但是补全为24位。
perl -le '{@a=unpack("a2"x12,sprintf("%024b\n",100));print join":", @a}'
00:00:00:00:00:00:00:00:01:10:01:00
unpack这个函数很简单,就是把二进制的数值拆分成字符串数组,两个数字组成一个字符串。
最后通过map把$a{"00"}="A";$a{"01"}="T";$a{"10"}="C";$a{"11"}="G";对应成hash值并且输出即可!
然后这个大神又提成了一个递归的办法来解决
[perl]
#!/usr/bin/perl
my $arr = [];
$arr->[$_] = 0 for (0..11);
my @b = ("A","C","G","T");
while(1){
print join "", map {$b[$_]} @$arr;
print "\n";
exit unless &count($arr,11);
}
sub count {
my $arr = shift;
my $x = shift;
return 0 if $x < 0;
if ($arr->[$x] < 3){
$arr->[$x]++;
return 1;
}
else{
$arr->[$x] = 0;
return &count($arr,$x - 1);
}
}
[/perl]
还有另外一个大神也提成了一个递归的算法解决,算法之道还是蛮有趣的
最后还补充一个也是计算机技巧的解决方案,也是群里的朋友提出来的。
[perl]
#!/usr/bin/perl -w
my @A = qw(A T G C);
my $N = 12;
foreach my $n (0..4**$N){
foreach my $index (0..$N){
print $A[$n%4];
$n = $n >>2;
}
print "\n";
}
[/perl]
这个是位运算,如果C语言基础学的好的同学很快就能看懂的。