8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

もっと遅いソート

Last updated at Posted at 2022-05-22

これは何?

2014年に 遅いソート という記事を書いた。
もっと遅くできるなと以前から思っていたんだけど、それを記事にした。

遅いソートの例

Wikipedia の ボゴソート に書いてある例、有名なもの、そして私案を挙げる

bubble sort

遅いソートアルゴリズムとして、おそらく最も有名。

ruby
def bubblesort!(s)
  (s.size).times do |i|
    (1...s.size).each do |j|
      s[j], s[j-1] = s[j-1], s[j] if s[j]<s[j-1]
    end
  end
  s
end

一般的には実用にならないぐらい遅いソートアルゴリズムの代表のような扱いだけれど、今回扱うアルゴリズムの中ではぶっちぎりに速い。1000件の整列が瞬きする間に終わるなんて、夢のようだ。

slow sort

Wikipedia に 記事 がある。

  1. 領域を前半と後半に分ける。
  2. 前半と後半をそれぞれ slow sort。
  3. 前半の末尾と後半の末尾を、必要なら入れ替える → 末尾が最大値となることが保証される。
  4. 末尾を除いた領域を slow sort。

というもの。末尾再帰でなければスタックをたくさん使うが、スタック・オーバーフローするようなデータ量だと計算が終わらないので問題にならないと思う。

ruby で書くとこんな感じ。

ruby
def slowsort!(s)
  impl = ->(i,j){
    return if j<=i
    m=(i+j)/2
    impl[i,m]
    impl[m+1,j]
    s[j],s[m] = s[m],s[j] if s[j]<s[m]
    impl[i,j-1]
  }
  impl[0,s.size-1]
  s
end

わりとひどいアルゴリズムに見えるけど、今回扱うアルゴリズムでは二番目に速い。

bogo sort

非実用的な遅いソートアルゴリズムとして、おそらく最も有名。
「適当に混ぜて、整列されていたら終了」というシンプルなアイディア。

ruby の場合とても簡単に書ける。

ruby
def bogosort(x)
  loop do
    s = x.shuffle
    return s if s.each_cons(2).all?{ |a,b| a<=b }
  end
end

乱数の質によっては整列できない(つまり、無限ループとなる)可能性がある。
ワーストケースは無限。

bozo sort

「整列していなければ、ランダムな2要素を入れ替える」を繰り返すだけというこちらもシンプルなアイディア。
こちらは C 言語などでも簡単に書けると思うけど、下記は ruby。

ruby
def bozosort!(x)
  loop do
    return s if s.each_cons(2).all?{ |a,b| a<=b }
    a,b = rand(x.size), rand(x.size)
    s[a], s[b] = s[b], s[a]
  end
end

bogo sort と同じく、乱数の質によっては整列できない(つまり、無限ループとなる)可能性がある。
直感的には bogo sort より遅そうだけど、動かしてみるとわりと同じぐらいに見える。

拙作(2014)

2014年に私が考えた計算。
もっと前に誰かが考えていると思うけど、探してないので私より先に発見した人がいるかどうかは知らない。

並べ方を全部列挙して、整列済みならそれを返すというもの。
bogo sort と似ているけど、 bogo sort と違って有限の時間で終わる。

ruby で書くと簡単。

ruby
def nabesort_2014(s)
  s.permutation(s.size){ |x|
    return x if x.each_cons(2).all?{ |a,b| a<=b }
  }
end

平均的な計算時間は bogo sort と同じだと思う。

拙作(2022)

2014年の記事を書いた後、 repeated_permutation にしたらもっと遅くなるよね、と誰かに言われたのか自分で気づいたのか覚えてない。

で、書いた。

ruby
def nabesort_2022(s)
  s.repeated_permutation(s.size){ |x|
    return x if (
      x.each_cons(2).all?{ |a,b| a<=b } && 
      x.all?{ |e| x.count(e)==s.count(e) }
    )
  }
end

終了条件として、できた列が整列済みか否かの他に、各要素の個数が入力の列と同じかどうもチェックする必要がある。
そのチェックに 最悪 $O(n^2)$ の計算を入れている。

もっと遅くすることはできるんだと思うけど、今回紹介する中では一番遅い。

オーダー

真剣に検討してないけど、下表ぐらいだと思う。

名前 typical case       best case   worst case   n=100 
bubble $O(n^2)$ 同左 同左 $10^4$
slow $O(exp(6n^{1/6}))$ ぐらいかな 同左 同左 $10^6$
bogo $O(n!)$ ぐらい $O(n)$ $O(∞)$ $10^{160}$
bozo $O(n!)$ ぐらいかな $O(n)$ $O(∞)$ $10^{160}$
鍋2014 $O(n!)$ ぐらい $O(n)$ ぐらい $O(n!)$ ぐらい $10^{160}$
鍋2020 $O(n^n)$ ぐらい $O(n^2)$ ぐらい $O(n^n)$ ぐらい $10^{200}$

たとえば。「$O(n^n)$ ぐらい」は、適当なあまりおおきくない定数 A があれば「$O(n^n)$」と「$O((n+A)^{n+A})$」の間のどこかじゃないかな、ぐらいの気持ち。

slow のオーダーはよくわからないんだけど、雑にフィットしてこれぐらい。
bozo もよくわからないんだけど、bogo と同じぐらいかなと思っている。

実測

シャッフル済みの要素数 9 の配列を 1回だけ整列するという、かなりしょぼい計算をした。
最初、要素数 10 にしたんだけど、10分たっても終わらなかったので 9 にした。

               user     system      total        real
    bubble   0.000012   0.000000   0.000012 (  0.000011)
      slow   0.000019   0.000001   0.000020 (  0.000020)
      bogo   1.092641   0.006429   1.099070 (  1.099211)
      bozo   1.069077   0.004266   1.073343 (  1.073448)
 nabe 2014   0.178649   0.001577   0.180226 (  0.180302)
 nabe 2022 185.339007   1.285942 186.624949 (186.634422)

nabe 2022 だと、9要素の整列に3分以上を要した模様。人間のほうが速い。
この調子だと、10要素は40分ぐらいかな。

まとめ

軽い気持ちで書いてみたら、思ったよりだいぶ遅くなった。

あと、Wikipedia の bogosort のページ に、Worstsort というもっと遅そうなのがあったけど、試せないほど遅そうなので試してない。

それと。
もし、もっと遅いアルゴリズムがあるという場合は、この記事のコメント欄でそのアルゴリズムを紹介するのではなく、別の記事を起こすことをおすすめする。

8
0
8

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?