マージソート(?)を書いてみた
Twitterのタイムラインに、
「マージソート」ってキーワードが流れてきたので、想像で書いてみた。
想像っていうと嘘になっちゃうんだけど、
たしか、正月あたりに「大人のピタゴラスイッチ」って番組で、
しめじを使って説明してた気がしたので、
あとは、タイムラインで見つけたいくつかのヒントを元に実装してみた。
ヒントはこんな感じ。
・再帰する
・省メモリ(らしい)
・「マージする」という言葉から連想
で、こうなった。
use strict;
use warnings;
use integer;
use v5.10;
say join( ',', my_sort(4,7,2,5,3,1,9,8,6) );
sub my_sort {
my @ary = @_;
merge_sort( \@ary, 2 );
return @ary;
}
sub merge_sort {
my ( $ary_ref, $n ) = @_;
print "---- n = $n ----\n";
dump_array( $ary_ref, $n );
my $cnt = scalar( @{$ary_ref} );
for (my $i=0; $i<$cnt; $i+=$n) {
my $a1 = $i;
my $b1 = $i + ($n / 2);
my $a2 = $a1 + ($n / 2);
my $b2 = $b1 + ($n / 2);
if ( $cnt <= $b1 ) {
last; # マージすべきグループがない
}
# 2グループのケツの添え字をクリップ
$b2 = $cnt if ( $cnt < $b2 );
# マージ先の選択
for (my $j=$b1; $j<$b2; $j++) {
say 'merge: ', $ary_ref->[$j];
for (my $k=$a1; $k<$b2; $k++) {
if ( $ary_ref->[$k] < $ary_ref->[$j] ) {
my $wk = $ary_ref->[$j];
for (my $l=$j; $k<$l; $l--) {
swap( $ary_ref, $l, $l - 1 );
}
dump_array( $ary_ref, $n );
# マージ先グループの次の要素は、
# マージしようとしている値と同じか、より小さい
last;
}
}
}
}
print "---- end ----\n\n";
if ( $n < scalar(@{$ary_ref}) ) {
merge_sort( $ary_ref, $n * 2 );
}
}
sub swap {
my ( $ary_ref, $a, $b ) = @_;
my $wk = $ary_ref->[$b];
$ary_ref->[$b] = $ary_ref->[$a];
$ary_ref->[$a] = $wk;
}
sub dump_array {
my ( $ary_ref, $n ) = @_;
my $cnt = scalar( @{$ary_ref} );
my $i = 0;
while ( $cnt ) {
print '[';
for (my $j=0; $j<$n; $j++) {
if ( 0 < $cnt ) {
print $ary_ref->[$i++];
$cnt--;
if ( 0 < $cnt and $j < ($n - 1) ) {
print ',';
}
}
}
print ']';
}
print "\n";
}
結果は、こんな感じ。
$ perl aaa.pl
---- n = 2 ----
[4,7][2,5][3,1][9,8][6]
merge: 7
[7,4][2,5][3,1][9,8][6]
merge: 5
[7,4][5,2][3,1][9,8][6]
merge: 1
merge: 8
---- end ----
---- n = 4 ----
[7,4,5,2][3,1,9,8][6]
merge: 5
[7,5,4,2][3,1,9,8][6]
merge: 2
merge: 9
[7,5,4,2][9,3,1,8][6]
merge: 8
[7,5,4,2][9,8,3,1][6]
---- end ----
---- n = 8 ----
[7,5,4,2,9,8,3,1][6]
merge: 9
[9,7,5,4,2,8,3,1][6]
merge: 8
[9,8,7,5,4,2,3,1][6]
merge: 3
[9,8,7,5,4,3,2,1][6]
merge: 1
---- end ----
---- n = 16 ----
[9,8,7,5,4,3,2,1,6]
merge: 6
[9,8,7,6,5,4,3,2,1]
---- end ----
9,8,7,6,5,4,3,2,1
日付が変わる前を目標に勢いで書いたので自信ないけど、
一応、ソートできたので載せておきます。
おしまい。
2013/09/06 追記
マージ先の探索範囲を狭くしていかないとアレですね。
という訳で、そのうち、ちゃんと調べてから書いてみようと思います。
Leave a Comment