perlで配列の引き算、再調査
1年前にも配列の引き算(Diffrence)を考えていたけれど。
http://dokechin.hatenablog.com/entry/2013/07/03/213407
tsucchiさんのブログにコストに関する記述が興味深い。 http://d.hatena.ne.jp/tsucchi1022/20090419/1240152657
my @array1 = (1,2,3,4,5); my @array2 = (3,4,5,6,7); @union = @intersection = @difference = (); %count = (); foreach $element (@array1, @array2) { $count{$element}++ } foreach $element (keys %count) { push @union, $element; push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element; } print "@union\n"; print "@intersection\n"; print "@difference\n";
ただ、このソースでの"difference"はオレンジ部分と青部分の和なので、 differenceとは違くて そこで、AとBのintersection(緑)を求めて Aと緑の"difference"を求めるとオレンジ部分(A-B)になる。 こんな感じです。
use strict; use warnings; my @array1 = (1,2,3,4,5); my @array2 = (3,4,5,6,7); my @minus = array_minus(\@array1,\@array2); print "@minus"; sub array_minus{ my ($array1_ref, $array2_ref) = @_; my @array1 = @$array1_ref; my @array2 = @$array2_ref; my @union1 = my @intersection1 = my @difference1 = (); my %count = (); foreach my $element (@array1, @array2) { $count{$element}++ } foreach my $element (keys %count) { push @union1, $element; push @{ $count{$element} > 1 ? \@intersection1 : \@difference1 }, $element; } my @union2 = my @intersection2 = my @difference2 = (); %count = (); foreach my $element (@array1, @intersection1) { $count{$element}++ } foreach my $element (keys %count) { push @union2, $element; push @{ $count{$element} > 1 ? \@intersection2 : \@difference2 }, $element; } return @difference2; }
私的にはよく使うので、ArrayのDifference(差)ができるCPANモジュールをご存じの方は教えて欲しいな・・
追記 papixさんにArray::Diffを教えていただきました。
さて、どうせ負けるだろうと、いざ、ベンチ勝負!
use strict; use warnings; use Benchmark qw/timethese cmpthese/; use Array::Diff; my @array1 = (1..10000); my @array2 = (2..9999); my $result = timethese(100, { array_minus => sub { my @minus = array_minus(\@array1,\@array2); }, array_diff => sub { my $diff = Array::Diff->diff( \@array1, \@array2 ); }, }); cmpthese $result;
Rate array_diff array_minus array_diff 11.1/s -- -41% array_minus 18.9/s 70% --
Array::Diffは増分も検知するから、比較してもあまり意味がないのですが、マイナスだけの演算に特化している分速いのかな。
さらに追記 ArrayあたりをキーワードにCPANを漁ると Acme::Toolsにminusという関数があり、これも私の求めている関数なんですが。。 ベンチした結果に驚きを隠せない。(マシンが先ほどのベンチとは違いますが)この性能差はかなりのもの。。
Rate array_diff array_minus acme_tools array_diff 1.16/s -- -49% -84% array_minus 2.26/s 94% -- -68% acme_tools 7.12/s 513% 216% --
Acme::Tools恐るべし・・
追記
Array::Utilsを発見した。 これは、Acme::Toolsと方式が似ている感じ。(ハッシュにして比較)