新ソートアルゴリズム「配列挿入ソート」だ!

(追記:2012-1-10) id:m11m さんのコメントによりこのソートはバケットソート*1と呼ばれる既知のソートアルゴリズムであることがわかりました ^^; 追記によりお詫び申し上げます


(追記:2012-1-12) 記事に対するアクセスが異常なので調べてみると、dankogai氏のネタにされていたという名誉を受けていたことが判明しました^^; 光栄です
404 Blog Not Found:algorithm - bucket sort - 比較しなければソートは相当速い

                                                                      • -

以前にスリープソートという
ソートアルゴリズムが発見されたよね


常識を覆すソートアルゴリズム!その名も"sleep sort"! - Islands in the byte stream


で僕はこれに対抗してランニングソート
っていうアルゴリズムを見つけたんだよ
まあ失敗したんだけど..


sleep sortに対抗してrunning sortだ!(Ruby版|失敗に終わる編) - hp12c


で今回また新たなソートアルゴリズムを発見したから
みんなに紹介するよ


その名も「配列挿入ソート」!


これはいわば挿入ソートの簡易版だよ


まずはRubyで挿入ソートを書いてみるよ
だいたいこんな感じになるよ

class Array
  def insert_sort
    inject([]) { |mem, var| mem.insert_with_order(var) }
  end

  def insert_with_order(item)
    pos = find_index { |n| item <= n } || length
    insert(pos, item)
  end
end

require "mathn"

l = Prime.take(10).sort_by { rand } # => [7, 2, 3, 11, 17, 19, 29, 5, 23, 13]

l.insert_sort # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]


これに対して配列挿入ソートはこうだよ

class Array
  def array_insert_sort
    inject([]) { |mem, var| mem[var] = var; mem }.compact
  end
end

require "mathn"

l = Prime.take(10).sort_by { rand } # => [7, 2, 3, 11, 17, 19, 29, 5, 23, 13]

l.array_insert_sort # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

なんてことはない
ただ要素を配列のインデックスに対応付けて
配置し直しただけだよ
この前投稿したRubyで連続数字をハイフンでつなぐよ
のときに気が付いたんだ


でちょっと改良すれば文字のソートにも対応できるんだよ

class Array
  def array_insert_sort
    inject([]) { |mem, var| mem[var.ord] = var; mem }.compact
  end
end

require "mathn"

l = Prime.take(10).sort_by { rand } # => [13, 3, 29, 5, 17, 2, 11, 7, 19, 23]
l.array_insert_sort # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

c = "The quick brown fox jumps over the lazy dog".scan(/\w/) # => ["T", "h", "e", "q", "u", "i", "c", "k", "b", "r", "o", "w", "n", "f", "o", "x", "j", "u", "m", "p", "s", "o", "v", "e", "r", "t", "h", "e", "l", "a", "z", "y", "d", "o", "g"]

c.array_insert_sort # => ["T", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]


まあ文字列と重複要素には使えないけどね..


念のため実行速度を見てみるね
比較対象はRuby標準のsort
挿入ソート クイックソートだよ
クイックソートの実装は次の通りだよ

class Array
  def quick_sort
    return self if length <= 1
    base = pop
    smaller, bigger = partition { |e| e < base }
    push base
    smaller.quick_sort + [base] + bigger.quick_sort
  end
end


さあベンチマークを取るよ

require "benchmark"

Benchmark.bmbm do |bm|
  l = Prime.take(10000).sort_by { rand }
  bm.report { l.sort }
  bm.report { l.quick_sort }
  bm.report { l.insert_sort }
  bm.report { l.array_insert_sort }
end

# >> Rehearsal ------------------------------------
# >>    0.000000   0.000000   0.000000 (  0.002726)
# >>    0.050000   0.000000   0.050000 (  0.049841)
# >>    3.560000   0.000000   3.560000 (  3.565532)
# >>    0.010000   0.010000   0.020000 (  0.007513)
# >> --------------------------- total: 3.630000sec
# >> 
# >>        user     system      total        real
# >>    0.000000   0.000000   0.000000 (  0.002579)
# >>    0.050000   0.000000   0.050000 (  0.049816)
# >>    3.550000   0.000000   3.550000 (  3.561074)
# >>    0.010000   0.000000   0.010000 (  0.006725)

こりゃ素晴らしい!