安定なソートと不安定なソート(後編)

最後は、「その安定/不安定判定がうまくいってるのは、たまたまじゃね?」
って思ってる勘の良い人のために。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use strict;
use warnings;
use v5.10;
 
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;
};
 
foreach my $n ( 8, 12, 16, 20 ) {
    say "--- n = $n ---";
 
    my @v = 1..$n;
    my @data = stable_shuffle( to_weighted(\@v, 4) );
    say join( ',', (map { $_->[0]; } @data) );
 
    @data = $shell_sort->( @data );
    say join( ',', (map { $_->[0]; } @data) );
 
    my @sorted = map { $_->[1]; } @data;
    my $ret = join(',', @v) eq join(',', @sorted);
 
    say 'shell_sort is ', ( $ret ? 'stable!' : 'not stable!' );
}
 
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
--- n = 8 ---
1,2,1,2,1,2,1,2
1,1,1,1,2,2,2,2
shell_sort is stable!
--- n = 12 ---
1,2,3,1,2,3,1,2,3,1,2,3
1,1,1,1,2,2,2,2,3,3,3,3
shell_sort is not stable!
--- n = 16 ---
1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4
1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4
shell_sort is stable!
--- n = 20 ---
1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5
1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5
shell_sort is not stable!

n=8で間隔が4の場合、4の段階で交換が行われず、
次のフェーズで挿入ソートになってしまう。
同様に、n=16の場合も間隔が13と4の段階で交換が行われず、
ただの挿入ソートになってしまう。

安定か不安定か判断するのに、
前回のように中途半端な数でグルーピングすればいんだけど、
今回みたいな特定の組み合わせだと判断できなくて、
ソートのアルゴリズムに関わらず、正確に判定するのって難しいですね。

おしまい。

Leave a Comment