2

次の手順で乱数を生成するつもりです。

  1. ファイルからデータを読み取る(<DATA>)
  2. 入力データラインと同じ数の乱数を生成します
  3. 乱数を2回生成しないでください。たとえば、ループ番号xで生成された乱数がそれ以前に作成されている場合は、乱数を再作成します。

これが私が持っている無限ループにつながるコードです。ロジックの何が問題になっていますか?どうすれば修正できますか?

#!/usr/bin/perl -w
use strict;
my %chrsize = ('chr1' =>249250621);

# For example case I have created the
# repository where a value has been inserted.
my %done =("chr1    182881372" => 1);

while ( <DATA> ) {
 chomp;
 next if (/^\#/);

 my ($chr,$pos) = split(/\s+/,$_);
 # this number has been generated before
 # with this: int(rand($chrsize{$chr}));
 # hence have to create other than this one
 my $newst =182881372;

 my $newpos = $chr ."\t".$newst;


 # recreate random number
 for (0...10){
     if ( $done{$newpos} ) {

            # INFINITE LOOP
            $newst = int(rand($chrsize{$chr}));
            redo;
     }
 }

 $done{$newpos}=1;
print "$newpos\n";

}


__DATA__
# In reality there are 20M of such lines
# name  positions
chr1    157705682
chr1    19492676
chr1    169660680
chr1    226586538
chr1    182881372
chr1    11246753
chr1    69961084
chr1    180227256
chr1    141449512
4

3 に答える 3

3

いくつかのエラーがありました:

  1. $newstあなたは毎回ループ内で設定していたので$newpos、新しい値をとることはありませんでした。
  2. 条件を再度チェックする前 forに実際に変更したことがないため、内部ループは意味がありませんでした。$newpos
  3. redo;内側のループに取り組んでいました。

redoこれは完全に回避する修正バージョンです。

更新:アルゴリズムを少し編集して、単純にしました。

 #!/usr/bin/perl -w
use strict;
my $chr1size = 249250621;

my %done;
my $newst;

while ( <DATA> ) {
    chomp;
    next if (/^\#/);
    my ($chr,$pos) = split(/\s+/,$_);

    my $newpos;
    #This will always run at least once
    do {
        $newst = int(rand($chr1size));
        $newpos = $chr ."\t".$newst;
    } while ( $done{$newpos} );

    $done{$newpos}=1;
    print "$newpos\n";
}

更新2:上記のアルゴリズムは機能しますが、20,000,000行では非常に遅くなります。これは、より高速であるはずの代替アプローチです(生成される乱数にはある種のパターンがありますが、ほとんどの状況ではおそらく問題ありません)。

#!/usr/bin/perl -w
use strict;
my $newst;

#make sure you have enough.  This is good if you have < 100,000,000 lines.
use List::Util qw/shuffle/;
my @rand_pieces = shuffle (0..10000);

my $pos1   = 0;
my $offset = 1;
while ( <DATA> ) {
    chomp;
    next if (/^\#/);
    my ($chr,$pos) = split(/\s+/,$_);

    $newst = $rand_pieces[$pos1] * 10000 + $rand_pieces[($pos1+$offset)%10000];
    my $newpos = $chr ."\t".$newst;

    $pos1++;
    if ($pos1 > $#rand_pieces) 
    {
        $pos1 = 0;
        $offset = ++$offset % 10000;
        if ($offset == 1) { die "Out of random numbers!"; } 
    }

    print "$newpos\n";
}
于 2012-10-19T08:16:08.600 に答える
1

次のようにループにカウンターを追加します。

my $counter = 0;
# recrate
for (0...10){
  if ( $done{$newpos} ) {
    # INFINITE LOOP
    $newst = int(rand($chr1size));
    redo if ++$counter < 100; # Safety counter
    # It will continue here if the above doesn't match and run out
    # eventually
  }
}
于 2012-10-19T07:38:04.243 に答える
1

無限ループを取り除くには、redonextに置き換えます。

http://www.tizag.com/perlT/perlwhile.php:「Redoは同じ反復を繰り返し実行します。」

次に、残りのロジックを修正する必要があるかもしれません;)。

于 2012-10-19T07:57:58.867 に答える