シェルソートを書いてみた(おまけ)
シェルソートのソート間隔に素数を使ってみました。
use strict; use warnings; use v5.10; use Array::Shuffle qw/shuffle_array/; use Time::HiRes qw/time/; my @data = 1..20000; shuffle_array( @data ); { my $cnt = scalar( @data ); my @prime = calc_prime( $cnt - 1 ); unshift @prime, 1; my $st = time(); my @sorted = shell_sort( \@data, \@prime ); say 'Prime : ', time() - $st; } { my $cnt = scalar( @data ); my @gap = ( 1 ); while ( $gap[-1] < $cnt ) { push @gap, ($gap[-1] * 2) + 1; } pop @gap; my $st = time(); my @sorted = shell_sort( \@data, \@gap ); say 'Knuth1: ', time() - $st; } { my $cnt = scalar( @data ); my @gap = ( 1 ); while ( $gap[-1] < $cnt ) { push @gap, ($gap[-1] * 3) + 1; } pop @gap; my $st = time(); my @sorted = shell_sort( \@data, \@gap ); say 'Knuth2: ', time() - $st; } sub calc_prime { my $n = shift; my @ret = (); if ( $n < 2 ) { return @ret; } my @wk = 0..$n; $wk[0] = -1; $wk[1] = -1; for (my $i=0; $i<=$n; $i++) { next if $wk[$i] < 0; for (my $j=($i+$i); $j<=$n; $j+=$i) { $wk[$j] = -1; } } return grep { $_ != -1; } @wk; } sub shell_sort { my ( $data_ref, $gap_ref ) = @_; my @data = @{$data_ref}; my $cnt = scalar( @data ); foreach my $gap ( reverse @{$gap_ref} ) { 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; }
実行結果はこんな感じ。
$ perl aaa.pl
Prime : 30.5675919055939
Knuth1: 0.555704116821289
Knuth2: 0.495223045349121
どの辺りから気付いてたかというと、
shell_sort
の$gap_ref
をデバッグ出力した辺り。
「あ、これダメだ。」って思いました。
ちなみに、shell_sort
の中で$gap_ref
の要素数だけ出力するとこんな感じ。
$ perl aaa.pl
scalar(@{$gap_ref) = 2263
Prime : 0.00201201438903809
scalar(@{$gap_ref) = 14
Knuth1: 0.00165200233459473
scalar(@{$gap_ref) = 9
Knuth2: 0.00163888931274414
クヌース先生は本当に偉大ですね。
おしまい。
Leave a Comment