2

CまたはPerlを使用して、ファイルのすべての要素を別のファイルのすべての要素と比較して、はるかに大きなデータを取得する方法は? ファイル 1 にはそのような数値が 100,000 個含まれており、ファイル 2 には 500,000 個の要素が含まれています。

配列内のすべての要素を分割して foreach 内で foreach を使用しました。perl では完全に機能しましたが、ファイル 1 のファイル 2 から単一の列の要素が出現するたびにチェックして出力するのにかかった時間は 40 分でした。そのような列は 28 あります。

時間を短縮する方法や、C などの別の言語を使用する方法はありますか?

ファイル 1:

0.1
0.11
0.12
0.13
0.14
0.15
0.16
0.17
0.18
0.19
0.2

ファイル 2:

0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.1 0.11    0.12    0.13    0.14    0.15    0.16    0.17    0.18    0.19    0.2 0.21    0.22    0.23    0.24    0.25    0.26    0.27    0.28
1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 1.1 1.11    1.12    1.13    1.14    1.15    1.16    1.17    1.18    1.19    1.2 1.21    1.22    1.23    1.24    1.25    1.26    1.27    1.28

編集:

期待される出力:

ファイル 2 の要素の場合、一致した場合は「列番号」を印刷し、そうでない場合は「0」を印刷します。

1  2  0  0  0  0  0  0  0  10  11  12  13  14  15  16  17  18  19  20  0   0  0  0  0  0  0  0   
0  0  0  0  0  0  0  0  0   0   0  0   0   0   0   0   0   0   0   0  0   0  0  0  0  0  0  0  

これが私が使用しているコードです。注: ファイル 1 の列ごとにファイル 2 をチェックし、 trueの場合は列番号を出力し、 falseの場合は '0'を出力します。28 の異なるファイルのすべての列の出力を出力します。

#!/usr/bin/perl-w
chomp($file = "File1.txt");
open(FH, $file);
@k_org = <FH>;
chomp($hspfile = 'file2.txt');
open(FH1, $hspfile);
@hsporg = <FH1>;
for $z (1 .. 28) {
  open(OUT, ">$z.txt");
  foreach (@hsporg) {
    $i = 0;
    @h_org = split('\t', $_);
    chomp ($h_org[0]);
    foreach(@k_org) {
      @orginfo = split('\t', $_);
      chomp($orginfo[0]);
      if($h_org[0] eq $orginfo[0]) {
        print OUT "$z\n";
        $i = 1;
        goto LABEL;
      } elsif ($h_org[0] ne $orginfo[0]) {
        if($h_org[0]=~/(\w+\s\w+)\s/) {
          if($orginfo[0] eq $1) {
            print  OUT "0";
            $i = 1;
            goto LABEL;
          }
        }
      }
    }
    if ($i == 0) {
      print OUT "0";
    }
    LABEL: 
  }
}
close FH;
close FH1;
close OUT;
4

3 に答える 3

5

ファイルの場合sort(1)は、1回のパスでチェックできます。(並べ替えを含めて) 数秒以上かかることはありません。

もう 1 つの方法は、file1 からすべての値をハッシュにロードすることです。特に file1 が大きい場合は、メモリを少し消費しますが、高速である必要があります (これも数秒以内です)。

私はそのような仕事には C よりも perl を選びますし、perl よりも C の方が得意です。この種の作業では、perl でコーディングする方がはるかに高速で、エラーが発生しにくく、十分に高速に実行できます。

于 2013-04-16T12:28:50.677 に答える
3

このスクリプトは、テスト ケースを実行します。期待される出力は客観的に間違っていることに注意してください。ファイル 2、1 行目、20 列目に値0.2が存在します。

#!perl

use 5.010; # just for `say`
use strict; use warnings;
use Test::More;

# define input files + expected outcome
my $file_1_contents = <<'_FILE1_';
0.1
0.11
0.12
0.13
0.14
0.15
0.16
0.17
0.18
0.19
0.2
_FILE1_

my $file_2_contents = <<'_FILE2_';
0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.1 0.11 0.12 0.13 0.14 0.15 0.16 0.17 0.18 0.19 0.2 0.21 0.22 0.23 0.24 0.25 0.26 0.27 0.28
1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 1.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.18 1.19 1.2 1.21 1.22 1.23 1.24 1.25 1.26 1.27 1.28
_FILE2_

my $expected_output = <<'_OUTPUT_';
1 2 0 0 0 0 0 0 0 10 11 12 13 14 15 16 17 18 19 20 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
_OUTPUT_

# open the filehandles
open my $file1, "<", \$file_1_contents or die "$!";
open my $file2, "<", \$file_2_contents or die "$!";
open my $expected, "<", \$expected_output or die "$!";

my %file1 = map { chomp; 0+$_ => undef } <$file1>;

while (<$file2>) {
    chomp;
    my @vals = split;
    # If value exists in file1, print the col number.
    my $line = join " " => map { exists $file1{0+$vals[$_]} ? $_+1 : 0 } 0 .. $#vals;
    chomp(my $expected_line = <$expected>);
    is $line, $expected_line;
}
done_testing;

まったく同じ出力を 28 個のファイルに出力するには、テスト コードを削除します。

say {$_} $line for @filehandles;

代わりは。

古い答え

あなたの既存のコードは非常に非効率的で単調です。問題を解決するお手伝いをさせてください。

まず、すべての Perl スクリプトを で開始します。use strict; use warnings;最新の perl (v10 以降) を使用している場合は、use 5.010;(またはバージョンが何であれ) 追加機能を有効にすることができます。

chomp呼び出しは変数を取り、現在の値 (通常は改行) を文字列の末尾から削除します$/。readline オペレーターはそれを行わないため、これは重要です。定数変数の宣言には適していません。むしろ、する

my $file   = "File1.txt"; 
my $hspfle = "File2.txt";

では、use strict変数を適切に宣言する必要があります。これは、 で行うことができますmy

ファイルを開くには、次のイディオムを使用する必要があります。

open my $fh, "<", $filename or die "Can't open $filename: $!";

代わりに、スクリプトの先頭にor die ...できます。use autodie

これにより、ファイルを開けない場合はスクリプトが中止され、理由が表示され ( $!)、明示的なオープン モードが指定されます (ここでは<= 読み取り)。これにより、ファイル名に特殊文字が含まれるバグが回避されます。

レキシカル ファイルハンドル (myベアワード ファイルハンドルとは対照的に、変数内) は適切なスコープを持ち、それ自体を閉じます。それらを使用する理由は他にもさまざまです。

このsplit関数は、最初の引数として文字列ではなく、正規表現を取ります。プログラムを詳しく調べると、split各要素が@hsporg28 回、各要素が@k_org28 × @hsporg 回であることがわかります。これは非常に遅く、事前に行うことができるため不要です。

条件が false の場合、再度明示的に false をテストする必要はありません。

if ($h_org[0] eq $orginfo[0]) {
  ...;
} elsif ($h_org[0] ne $orginfo[0]) {
  ...;
}

$a ne $bまったく同じnot $a eq $bです。

Perl で a を使用するのはかなり一義的でありgoto、どこかのラベルへのジャンプも特に高速ではありません。ラベルは主にループ制御に使用されます。

# random example
LOOP: for my $i (1 .. 10) {
  for my $j (1 .. 5) {
    next      if     $i == $j; # start next iteration of current loop
    next LOOP if 2 * $i == $j; # start next iteration of labeled loop
    last LOOP if $i + $j == 13;# like `break` in C
  }

redoループ制御動詞は に似ていnextますが、ループ条件がある場合でも再チェックしません。

これらのループ制御機能と、囲んでいるループから抜け出す機能があるため、フラグを維持したり複雑な goto を実行したりする必要はほとんどありません。


実際のアルゴリズムをあまり修正せずに、スクリプトをクリーンアップしたバージョンを次に示します。

#!/usr/bin/perl

use strict; use warnings;
use autodie; # automatic error messages

my ($file, $hspfile) = ("File1.txt", "file2.txt");
open my $fh1, "<", $file;
open my $fh2, "<", $hspfile;

my @k_org  = <$fh1>;
my @hsporg = <$fh2>;

# Presplit the contents of the arrays:
for my $arr (\@k_org, \@hsporg) {
  for (@$arr) {
    chomp;
    $_ = [ split /\t/ ]; # put an *anonymous arrayref* into each slot
  }
}

my $output_files = 28;

for my $z (1 .. $output_files) {
  open my $out, ">", "$z.txt";

  H_ORG:
  for my $h_org (@hsporg) {
    my $i = 0;

    ORGINFO:
    for my $orginfo (@k_org) {
      # elements in array references are accessed like $arrayref->[$i]
      if($h_org->[0] eq $orginfo->[0]) {
        print $out "$z\n";
        $i = 1;
        last ORGINFO; # break out of this loop
      } elsif($h_org->[0] =~ /(\w+\s\w+)\s/ and $orginfo->[0] eq $1) {
        print $out "0";
        $i = 1;
        last ORGINFO;
      }
    }
    print $out "0" if not $i;
  }
}

# filehandles are closed automatically.

ここで、さらに最適化できます。各行では、最初の要素のみを使用します。これは、残りを保存する必要がないことを意味します。

...;
  for (@$arr) {
    chomp;
    $_ = (split /\t/, $_, 2)[0]; # save just the first element
  }
...;
    ORGINFO:
    for my $orginfo (@k_org) {
      # elements in array references are accessed like $arrayref->[$i]
      if($h_org eq $orginfo) {
        ...;
      } elsif($h_org =~ /(\w+\s\w+)\s/ and $orginfo eq $1) {
        ...;
      }
    }

また、スカラーへのアクセスは、配列要素へのアクセスよりも少し高速です。

split結果のフラグメントの数を制限する3 番目の引数。最初のフィールドのみに関心があるため、残りも分割する必要はありません。

次にlast、ループから抜け出しORGINFO、フラグをチェックします。H_ORGこれは不要です。フラグを設定する代わりに、ループの次の繰り返しに直接ジャンプできます。ループから自然に脱落した場合ORGINFO、フラグが設定されていないことが保証されるため、printとにかく実行できます。

  H_ORG:
  for my $h_org (@hsporg) {
    for my $orginfo (@k_org) {
      if($h_org eq $orginfo) {
        print $out "$z\n";
        next H_ORG;
      } elsif($h_org =~ /(\w+\s\w+)\s/ and $orginfo eq $1) {
        print $out "0";
        next H_ORG;
      }
    }
    print $out "0";
  }

次に、同じデータを 28 回比較して、異なるファイルに出力します。ベター: 2 つのサブルーチンprint_indexとを定義しprint_zeroます。これらの内部で、出力ファイルハンドルをループします。

# make this initialization *before* you use the subs!
my @filehandles = map {open my $fh, ">", "$_.txt"; $fh} 1 .. $output_files;

...; # the H_ORG loop

sub print_index {
  for my $i (0 .. $#filehandles) {
    print {$filehandles[$i]} $i+1, "\n";
  }
}
sub print_zero {
  print {$_} 0 for @filehandles;
}

それで:

  # no enclosing $z loop!
  H_ORG:
  for my $h_org (@hsporg) {
    for my $orginfo (@k_org) {
      if($h_org eq $orginfo) {
        print_index()
        next H_ORG;
      } elsif($h_org =~ /(\w+\s\w+)\s/ and $orginfo eq $1) {
        print_zero();
        next H_ORG;
      }
    }
    print_zero();
  }

これにより、一致しないことがすでにわかっているデータのチェックを回避できます。

于 2013-04-17T07:06:25.200 に答える
1

Cでは、「qsort」および「bsearch」関数を使用してみることができます

まず、ファイルを配列にロードする必要があります。

qsort() を実行するよりも(要素に順序があることが確実でない限り)。bsearch() を使用して、配列に対してバイナリ検索を実行します。

http://linux.die.net/man/3/bsearch

これは、すべての要素を 1 つずつチェックするよりもはるかに高速です。

二分探索がまだ存在しない場合は、perl で実装を試みることができます。これは単純なアルゴリズムです。

于 2013-04-16T12:35:53.693 に答える