シェルソートを書いてみた(前編)
挿入ソートの改良版であるシェルソートを書いてみた。
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