シェルソートを書いてみた(おまけ)

シェルソートのソート間隔に素数を使ってみました。

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