하드 코딩 루프없이 여러 목록의 조합을 어떻게 만들 수 있습니까?

StackOverflow https://stackoverflow.com/questions/1442965

  •  22-07-2019
  •  | 
  •  

문제

다음과 같은 데이터가 있습니다.

    my @homopol = (
                   ["T","C","CC","G"],  # part1
                   ["T","TT","C","G","A"], #part2
                   ["C","CCC","G"], #part3 ...upto part K=~50
                  );


    my @prob = ([1.00,0.63,0.002,1.00,0.83],
                [0.72,0.03,1.00, 0.85,1.00],
                [1.00,0.97,0.02]);


   # Note also that the dimension of @homopol is always exactly the same with @prob.
   # Although number of elements can differ from 'part' to 'part'.

내가하고 싶은 것은

  1. 요소의 모든 조합을 생성합니다 part1 전역 partK
  2. 해당 요소의 제품을 찾으십시오 @prob.

따라서 결국 우리는이 출력을 얻기를 희망합니다.

T-T-C  1 x 0.72 x 1 = 0.720
T-T-CCC     1 x 0.72 x 0.97 = 0.698
T-T-G  1 x 0.72 x 0.02 = 0.014
...
G-G-G  1 x 0.85 x 0.02 = 0.017
G-A-C  1 x 1 x 1 = 1.000
G-A-CCC     1 x 1 x 0.97 = 0.970
G-A-G  1 x 1 x 0.02 = 0.020

문제는 다음과 같은 코드가 루프를 하드 코딩하여이를 수행한다는 것입니다. 부분의 수 이후 @homopol IS는 다양하고 클 수 있으며 (예 : ~ K = 50), 동일한 결과를 얻을 수있는 유연하고 컴팩트 한 방법이 필요합니다. 있어요? 나는 사용하려고 생각했다 알고리즘 :: 루프, 그러나 어떻게 달성하는지 잘 모르겠습니다.

use strict;
use Data::Dumper;
use Carp;


my @homopol = (["T","C","CC","G"],
               ["T","TT","C","G","A"],
               ["C","CCC","G"]);


my @prob = ([1.00,0.63,0.002,1.00,0.83],
            [0.72,0.03,1.00, 0.85,1.00],
            [1.00,0.97,0.02]);



my $i_of_part1 = -1;
foreach my $base_part1 ( @{ $homopol[0] } ) {
    $i_of_part1++;
    my $probpart1 = $prob[0]->[$i_of_part1];

    my $i_of_part2 =-1;
    foreach my $base_part2 ( @{ $homopol[1] } ) {
        $i_of_part2++;
        my $probpart2 = $prob[1]->[$i_of_part2];

        my $i_of_part3 = -1;
        foreach my $base_part3 ( @{ $homopol[2] } ) {
            $i_of_part3++;
            my $probpart3 = $prob[2]->[$i_of_part3];

            my $nstr = $base_part1."".$base_part2."".$base_part3;
            my $prob_prod = sprintf("%.3f",$probpart1 * $probpart2 *$probpart3);

            print "$base_part1-$base_part2-$base_part3 \t";
            print "$probpart1 x $probpart2 x $probpart3 = $prob_prod\n";

        }
    }
}
도움이 되었습니까?

해결책

추천합니다 Set::CrossProduct, 모든 세트의 크로스 곱을 산출하기위한 반복자를 만듭니다. 반복기를 사용하기 때문에 모든 조합을 미리 생성 할 필요는 없습니다. 오히려, 그것은 각각의 주문형을 산출합니다.

use strict;
use warnings;
use Set::CrossProduct;

my @homopol = (
    [qw(T C CC G)],
    [qw(T TT C G A)],
    [qw(C CCC G)], 
);

my @prob = (
    [1.00,0.63,0.002,1.00],
    [0.72,0.03,1.00, 0.85,1.00],
    [1.00,0.97,0.02],
);

# Prepare by storing the data in a list of lists of pairs.
my @combined;
for my $i (0 .. $#homopol){
    push @combined, [];
    push @{$combined[-1]}, [$homopol[$i][$_], $prob[$i][$_]]
        for 0 .. @{$homopol[$i]} - 1;
};

my $iterator = Set::CrossProduct->new([ @combined ]);
while( my $tuple = $iterator->get ){
    my @h = map { $_->[0] } @$tuple;
    my @p = map { $_->[1] } @$tuple;
    my $product = 1;
    $product *= $_ for @p;
    print join('-', @h), ' ', join(' x ', @p), ' = ', $product, "\n";
}

다른 팁

사용하는 솔루션 알고리즘 :: 루프 입력 데이터를 변경하지 않고 다음과 같은 것 같습니다.

use Algorithm::Loops;

# Turns ([a, b, c], [d, e], ...) into ([0, 1, 2], [0, 1], ...)
my @lists_of_indices = map { [ 0 .. @$_ ] } @homopol;

NestedLoops( [ @lists_of_indices ], sub {
  my @indices = @_;
  my $prob_prod = 1; # Multiplicative identity
  my @base_string;
  my @prob_string;
  for my $n (0 .. $#indices) {
    push @base_string, $hompol[$n][ $indices[$n] ];
    push @prob_string, sprintf("%.3f", $prob[$n][ $indices[$n] ]);
    $prob_prod *= $prob[$n][ $indices[$n] ];
  }
  print join "-", @base_string; print "\t";
  print join "x", @prob_string; print " = ";
  printf "%.3f\n", $prob_prod;
});

그러나 나는 당신이 실제로 구조를 하나 더 변경하여 코드를 더 명확하게 만들 수 있다고 생각합니다.

[ 
  { T => 1.00, C => 0.63, CC => 0.002, G => 0.83 },
  { T => 0.72, TT => 0.03, ... },
  ...
]

병렬 데이터 구조가 없으면 사용 가능한 기본 시퀀스를 단순히 반복하고 인덱스를 반복 한 다음 두 개의 다른 장소에서 해당 지수를 찾을 수 있습니다.

재귀를 사용하지 않는 이유는 무엇입니까? 깊이를 매개 변수로 전달하고 함수를 루프 내부 깊이+1으로 호출하십시오.

@homopol 배열 (n say)과 동일한 길이의 indicies 배열을 만들어보고있는 조합을 추적하여 수행 할 수 있습니다. 실제로이 배열은 기본 N의 숫자와 같으며 요소는 숫자입니다. 기본 N, 예를 들어 (0 0 ... 0), (0 0 ... 1), ..., (0 0 ... N- 1), (0 0 ... 1 0), ....

접근 1 : 지수에서 계산

homopol에서 길이의 생성물을 계산합니다 (길이 1 * length2 * ... * lengthn). 그런 다음 0에서 제품으로 반복하십시오. 이제 원하는 지수는 I % 길이 1, (i / length1) % length2, (i / length1 / length2) % length3, ...

접근 2 : 재귀

나는 그것에 맞았다. Nikie의 대답을 보았다. :-)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top