安定なソートと不安定なソート(中編)
Perlのソートと挿入ソートとシェルソートの性質を確認してみた。
use strict; use warnings; use v5.10; my $perl_sort = sub { return sort { $a->[0] <=> $b->[0] } @_; }; my $insert_sort = sub { my @data = @_; my $cnt = scalar( @data ); for (my $i=1; $i<$cnt; $i++) { my $wk = $data[$i]; my $j = $i; for (; 0<$j; $j--) { if ( $wk->[0] < $data[$j-1]->[0] ) { $data[$j] = $data[$j-1]; } else { last; } } $data[$j] = $wk; } return @data; }; my $shell_sort = sub { my @data = @_; my $cnt = scalar( @data ); my $gap = 1; while ( $gap < $cnt ) { $gap = ($gap * 3) + 1; } while ( ($gap = ($gap - 1) / 3) ) { for (my $i=$gap; $i<$cnt; $i++) { my $wk = $data[$i]; my $j = $i; for (; $gap<$j; $j-=$gap) { if ( $wk->[0] < $data[$j-$gap]->[0] ) { $data[$j] = $data[$j-$gap]; } else { last; } } $data[$j] = $wk; } } return @data; }; my %h = ( perl => $perl_sort, insert => $insert_sort, shell => $shell_sort ); foreach my $k ( keys %h ) { say "${k}_sort is " . ( is_stable($h{$k}) ? 'stable!' : 'not stable!' ); } sub is_stable { my $func = shift; my @v = 1..16; my @data = stable_shuffle( to_weighted(\@v, 3) ); say join( ',', (map { $_->[0]; } @data) ); say join( ',', (map { $_->[1]; } @data) ); @data = $func->( @data ); say join( ',', (map { $_->[0]; } @data) ); say join( ',', (map { $_->[1]; } @data) ); my @sorted = map { $_->[1]; } @data; return join(',', @v) eq join(',', @sorted); } sub stable_shuffle { my @tmp = @_; my @src = (); while ( @tmp ) { my @wk = ( shift @tmp ); if ( @tmp ) { while ( $wk[0]->[0] == $tmp[0]->[0] ) { push @wk, ( shift @tmp ); last unless @tmp; } } push @src, \@wk; } my @dst = (); while ( @src ) { foreach ( @src ) { push @dst, shift @{$_}; } @src = grep { @{$_}; } @src; } return @dst; } sub to_weighted { my ( $v_ref, $interval ) = @_; my $i = 0; my $w = 1; my @data = (); foreach ( @{$v_ref} ) { push @data, [ $w, $_ ]; $i++; if ( $interval <= $i ) { $w++; $i = 0; } } return @data; }
実行結果はこんな感じ。
$ perl aaa.pl
1,2,3,4,5,6,1,2,3,4,5,1,2,3,4,5
1,4,7,10,13,16,2,5,8,11,14,3,6,9,12,15
1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
perl_sort is stable!
1,2,3,4,5,6,1,2,3,4,5,1,2,3,4,5
1,4,7,10,13,16,2,5,8,11,14,3,6,9,12,15
1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
insert_sort is stable!
1,2,3,4,5,6,1,2,3,4,5,1,2,3,4,5
1,4,7,10,13,16,2,5,8,11,14,3,6,9,12,15
1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6
1,2,3,4,6,5,9,7,8,11,12,10,13,14,15,16
shell_sort is not stable!
この結果を見ると、シェルソートだけが不安定なのが分かる。
昨日作った「安定なシャッフル」は、stable_shuffle
って関数名にして、
順番にマージするだけにした。
っていうのも、ランダムに処理すると何も変わらない場合も考えられるので、
今回の実装の方が安全な気がする。
あと気付いたのは、
今まで安定だと思ってた挿入ソートが不安定に判定されて、
見直してみたら交換する条件にバグが見つかって、
過去に書いた挿入ソートの記事から見直す必要がある。。。(*1)
続く。
(*1) 過去を記事を、すべて修正しました。
Leave a Comment