シェルソートを書いてみた(前編)
挿入ソートの改良版であるシェルソートを書いてみた。
use strict; use warnings; use v5.10; use Time::HiRes qw/time/; use Benchmark qw/cmpthese/; { my @data = 1..2000; printf( "--- asc (size = %d)---\n", scalar(@data) ); { my $st = time(); my @sorted = shell_sort( @data ); say 'shell :', time() - $st; } { my $st = time(); my @sorted = insert_sort( @data ); say 'insert:', time() - $st; } } { my @data = reverse 1..50000; printf( "--- desc (size = %d)---\n", scalar(@data) ); { my $st = time(); my @sorted = shell_sort( @data ); say 'shell :', time() - $st; } { my $st = time(); my @sorted = insert_sort( @data ); say 'insert:', time() - $st; } } sub shell_sort { my @data = @_; my $cnt = scalar( @data ); my $gap = $cnt; while ( ($gap = int($gap / 2)) ) { for (my $i=$gap; $i<$cnt; $i++) { my $wk = $data[$i]; my $j = $i; for (; $gap<=$j; $j-=$gap) { if ( $wk <= $data[$j-$gap] ) { last; } else { $data[$j] = $data[$j-$gap]; } } $data[$j] = $wk; } } return @data; } sub insert_sort { my @data = @_; my $cnt = scalar( @data ); { for (my $i=1; $i<$cnt; $i++) { my $wk = $data[$i]; my $j = $i; for (; 1<=$j; $j--) { if ( $wk <= $data[$j-1] ) { last; } else { $data[$j] = $data[$j-1]; } } $data[$j] = $wk; } } return @data; }
結果はこんな感じ。
$ perl aaa.pl
--- asc (size = 2000)---
shell :0.0280210971832275
insert:1.30933809280396
--- desc (size = 50000)---
shell :0.788571834564209
insert:0.0688240528106689
挿入ソートとの違いは、ソースを比較すると分かると思うけど、
まず、飛ばし飛ばしで挿入ソートを行って、
その間隔を狭めながら挿入ソートを繰り返して、
最後は通常の挿入ソートを行っている。
最初は、こんなんで速くなるのかと思ったけど、
あまりにも改善され過ぎてて、あとでバグを見つけて直すかも知れない。(*1)(*2)
あと、書いてる最中に、ソート済みのデータに弱いんじゃ?と思って、
試してみた所、案の定、挿入ソートに負けている。
ただ、今回実装したシェルソートは、まだ改善の余地があるらしいけど、
次回は、なぜ、こんなに速いのか?を紐解いてみようと思う。
続く。
(*1) 公開した直後にバグを見つけたので直した。
(*2) その数十分後にバグを見つけたので直した。
Leave a Comment