ランダムコンパレータを受け入れるソートアルゴリズム


22

一般的な並べ替えアルゴリズムは、通常、並べ替えるデータのセットと、2つの個別の要素を比較できる比較関数を受け取ります。コンパレータが順序関係¹の場合、アルゴリズムの出力はソートされたリスト/配列です。

私は、どのソートアルゴリズムが実際には順序関係ではないコンパレーター(特に、各比較でランダムな結果を返すもの)で動作するのか疑問に思っています。「作業」とは、ここでは、入力の順列を返し続け、常に最悪のシナリオに低下したり、無限ループに入ったり、要素が欠落したりするのではなく、通常引用された時間の複雑さで実行されることを意味します。ただし、結果の順序は定義されていません。さらに良いことに、コンパレータがコインフリップの場合、結果の順序は均一な分布になります。

私の大まかな精神的な計算から、マージソートはこれで問題なく、同じ実行時コストを維持し、公平なランダムな順序を生成するように見えます。しかし、クイックソートのようなものは退化する可能性があり、おそらく終了せず、公平ではないと思います。

ランダムコンパレータで説明されているように、他のどのソートアルゴリズム(マージソート以外)が機能しますか?


  1. 参考のために、コンパレータは、適切な関数(決定論的)であり、順序関係の公理を満たす場合、順序関係になります。

    • それは決定論的です。compare(a,b)特定の場合ab常に同じ結果を返します。
    • 推移的です: compare(a,b) and compare(b,c) implies compare( a,c )
    • それは反対称です compare(a,b) and compare(b,a) implies a == b

(すべての入力要素が別個であると想定しているため、反射性は問題ではありません。)

ランダムコンパレータは、これらのすべてのルールに違反します。ただし、順序関係ではないコンパレータはまだランダムではありません(たとえば、おそらく1つのルールに違反している可能性があり、セット内の特定の要素のみに違反している可能性があります)。


(1)比較機能が安定しているとはどういう意味ですか?(2)「非安定」と「ランダム」は同義語ですか?
伊藤剛

「最悪のシナリオに低下するのではなく、通常引用された時間の複雑さで実行します」-通常引用された時間の複雑さ最悪です!「順序は公平なランダムな順序になります」コンパレーターも均一だと思いますか?
ラファエル

おそらく正式な理論ではなく、実際には(プログラミング言語)償却された時間に多くのことを引用しています。たとえば、クイックソートはしばしばとして表示されますが、実際にはO n 2です。O(logn)O(n2)
edA-qa mort-ora-y

4
(1)あなたが平均:EDA-qamort-ORA-Y @ ではなく、O ログn個。(2)それは「償却時間」の意味ではありません。あなたは「予想時間」、またはそれより形式的には「典型的な時間」を意味します。O(nlogn)O(logn)
JeffE

1
上記のより興味深い質問(誰もが)に対処した人はいません。どのソートアルゴリズム(存在する場合)には、コンパレータがコインフリップである場合、結果は一様な順列であるという特性があります。
ジョー

回答:


13

したがって、基本的には、次のような比較関数が与えられた場合に、平均的なケースから低下しないソートアルゴリズムがあるかどうかを知りたいと思います。

int Compare(object a, object b) { return Random.Next(-1,1); }

...ここでRandom.Next()は、指定された下限と上限の間にランダムに生成された整数を生成するメソッドです。

答えは、実際には、ほとんどの基本的なソートアルゴリズムが平均的なケースに従って実行されるということです。なぜなら、以下の2つの条件の少なくとも1つに従うからです。

  1. 2つの一意の要素間の比較がソートで2回行われることはありません。
  2. ソートの各反復で、少なくとも1つの要素の正しい位置が決定されるため、その要素は二度と比較されません。

たとえば、SelectionSortは、並べ替えられていない要素のサブリストを反復処理し、「最小」および/または「最大」要素を見つけて(各要素をこれまでの最大要素と比較することにより)、正しい位置に配置して繰り返します。その結果、非決定的なコンパレータを使用しても、アルゴリズムはすべての反復の終わりに、最小または最大と考えられる値を見つけ、それを決定しようとしている位置の要素と交換し、考慮しませんただし、このプロセス中にAとBを複数回比較できます(最も極端な例として、逆の順序で並べ替えられた配列でSelectionSortのいくつかのパスを検討します)。したがって、条件1に違反します。 。

MergeSortは2ではなく条件1に従います。サブ配列がマージされると、同じサブ配列(左側または右側)の要素は互いに比較されません。これは、配列のその側の要素が相互に整然と並んでいると既に判断されているためです。アルゴリズムは、各サブアレイの最もマージされていない要素を他と比較するだけで、どちらがより小さく、マージされたリストで次に進むべきかを決定します。つまり、2つの一意のオブジェクトAとBが最大1回互いに比較されますが、完全なコレクション内の特定の要素の「最終」インデックスは、アルゴリズムが完了するまでわかりません。

InsertionSortは、全体的な戦略と複雑さがSelectionSortに似ている場合でも、条件1のみに従います。並べ替えられていない各要素は、検査対象の要素よりも小さい要素が見つかるまで、最も大きいものから順に並べ替えられた要素と比較されます。その時点で要素が挿入され、次の要素が考慮されます。その結果、AとBの相対的な順序は1つの比較によって決定され、そのAとBの間のさらなる比較は実行されませんが、すべての要素が考慮されるまで、要素の最終位置はわかりません。

QuickSortは両方に従います条件。各レベルで、ピボットが選択され、「左側」にピボットよりも小さい要素が含まれ、「右側」にピボットよりも大きい要素が含まれるように配置されます。そのレベルの結果は、QuickSort(左)+ピボット+ QuickSort(右)です。これは、基本的にピボット要素の位置がわかっていることを意味します(左側の長さよりも1つのインデックスが大きい)。ピボットとして選択された後(以前のピボット要素と比較された可能性がありますが、これらの要素も既知であり、サブアレイに含まれていません)、およびピボットの反対側にあるAおよびBは決してありません比較しました。純粋なQuickSortのほとんどの実装では、基本ケースは1つの要素であり、その時点で現在のインデックスは最終インデックスであり、それ以上の比較は行われません。

(2/3)N1)。コンパレーターの結果の最大絶対値が増加すると、いずれかの比較が負またはゼロを返す確率が0.5に向かって減少し、アルゴリズムを終了する可能性がはるかに低くなります(99コインの可能性がすべての着陸ヘッドを反転します) 、これは基本的にこれが要約するものであり、1.2 * 10 30分の 1です)

長時間の編集:ランダムコンパレータを組み込んだ、禁止事項の例として特別に設計された「ソート」がいくつかあります。おそらく最も有名なのはBogoSortです。「リストが与えられた場合、リストの順序が正しくない場合は、リストをシャッフルしてもう一度確認してください」。理論的には、上記の「最適化されていないBubbleSort」と同様に、最終的に値の正しい順列にヒットしますが、平均ケースは階乗時間(N!/ 2)であり、誕生日の問題のため(十分なランダム順列の後、ユニークなものよりも重複する順列に遭遇する可能性が高くなります)アルゴリズムが時間的に制限されていないため、アルゴリズムが公式に完了しない可能性はゼロではありません。


条件2はクイックソートもカバーしますか?または、各反復が最後の反復よりも小さいという3番目の条件になります。
edA-qa mort-ora-y

私の考えでは、QuickSortは両方の条件でカバーされます。効率的なQuickSortsでは、ピボットを選択し、各要素をそれと比較して、ピボットの間違った「サイド」にある要素を交換します。要素が配置されると、関数はQuickSort(左)+ピボット+ QuickSort(右)を返し、ピボットは下位レベルに渡されません。したがって、両方の条件が当てはまります。一意のaとbを2回以上比較することはなく、他の要素の配置が完了するまでにピボットのインデックスを決定しました。
キース

すばらしい答えですが、BubbleSortについては同意しません。一貫性のあるコンパレータを使用する場合、i回目の反復で、BubbleSortはi-1の最後の要素が最終位置にあることを認識し、BubbleSortの合理的な実装は反復ごとに少ない要素を通過するため、n回の反復後に停止する必要があります。
ボリスTrayvas

さらに考えた後、私はあなたに同意する傾向があります。Xが通過した後に条件2を使用すると、各パス上の問題空間を減らすことができますので、効率的なアルゴリズムは従うだろうので、最大のX値は、私が編集しましょう、彼らの適切な場所にある
KeithS

Quicksortの実装には注意する必要があります。ピボットまたはピボットより大きい要素に遭遇すると、ピボット以上の要素の検索が終了するという仮定があります。必ずしもそうとは限りません。
gnasher729

10

O(n2)

n


編集:私が最初に考えたように、問題はより興味深いので、さらにコメントがあります:

comparecompare(x,y)=true1/2false1/2

insert x [] = [x]
insert x y:ys = if x < y then x:y:ys
                else y:insert x ys

sort_aux l e = match l with
                 [] -> e
                 x:xs -> sort_aux xs (insert x ys)

sort l = sort_aux l []

k=1nf(k)nlf(k)insertk:

compare

i=1ki2ii=1i2i=2

O(2n)O(n2)

この均一な比較機能を前提に、他のさまざまなアルゴリズムの平均実行時間を計算するのは楽しいでしょう。


同じ要素がピボットとして複数回選択されると、クイックソートは比較を繰り返すことができます(リスト内で複数回発生する場合があります)。
ラファエル

2
@Raphael:私の言葉の選択は貧弱でした。Quicksort では2回以上出現しない要素の出現を繰り返し比較することを意味していました。
コーディ

1
@Gilles:私は間違っているかもしれませんが、ほとんどのソートアルゴリズムの実行には、比較の推移性が重要であるとは考えていません。正確きっと、それは質問の対象ではなかったです。
コーディ

@Gilles:OPは、実際にソートするアルゴリズムについて尋ねていません。彼は、すべての比較がコインフリップに置き換えられたときに標準のソートアルゴリズムに何が起こるかについて尋ねています。結果のアルゴリズムはソートされませんが(確率が小さい場合を除く)、まだ明確に定義されたアルゴリズムです。
ジェフ

@JeffE私は今それを理解しています。それは私が最初に質問を読んだ方法ではありませんが、質問者のコメントを考えると、それは意図されたものです。
ジル 'SO-悪であるのをやめる'

2

フェアランダムコンパレータを備えたマージソートはフェアではありません。証拠はありませんが、非常に強力な経験的証拠があります。(公平とは、均一に分布していることを意味します。)

module Main where

import Control.Monad
import Data.Map (Map)
import qualified Data.Map as Map
import System.Random (randomIO)

--------------------------------------------------------------------------------

main :: IO ()
main = do
  let xs = [0..9]
  xss <- replicateM 100000 (msortRand xs)
  print $ countFrequencies xss

msortRand :: [a] -> IO [a]
msortRand = msort (\_ _ -> randomIO)

countFrequencies :: (Ord a) => [[a]] -> [Map a Int]
countFrequencies [] = []
countFrequencies xss = foldr (\k m -> Map.insertWith (+) k 1 m) Map.empty ys : countFrequencies wss
  where
    ys = map head xss
    zss = map tail xss
    wss = if head zss == []
      then []
      else zss

--------------------------------------------------------------------------------

msort :: (Monad m) => (a -> a -> m Bool) -> [a] -> m [a]
msort (<) [] = return []
msort (<) [x] = return [x]
msort (<) xs = do
  ys' <- msort (<) ys
  zs' <- msort (<) zs
  merge (<) ys' zs'
  where
    (ys, zs) = split xs

merge :: (Monad m) => (a -> a -> m Bool) -> [a] -> [a] -> m [a]
merge (<) [] ys = return ys
merge (<) xs [] = return xs
merge (<) (x:xs) (y:ys) = do
  bool <- x < y
  if bool
    then liftM (x:) $ merge (<) xs (y:ys)
        else liftM (y:) $ merge (<) (x:xs) ys

split :: [a] -> ([a], [a])
split [] = ([], [])
split [x] = ([x], [])
split (x:y:zs) = (x:xs, y:ys)
  where
    (xs, ys) = split zs

HaskellまたはCamlは今流行になっていますか?
Yai0Phah

何も思いつきません。しかし、Haskellは私のお気に入りの言語なので、これをプログラミングしました。パターンマッチングによりこれが容易になりました。
トーマスエディング

0

非常に関連する質問は、Christiansen、Danilenko、Dylusによるあらゆる種類の順列(機能的真珠)で回答されています。それらはリストモナドでソートアルゴリズムを実行し、本質的に非決定性をシミュレートし、与えられた入力リストのすべての順列を返します。興味深い特性は、各順列が1回だけ返されることです。

要約からの引用:

...

このホワイトペーパーでは、非決定論と異なる観点からの並べ替えの組み合わせを検討します。並べ替え関数が与えられた場合、それを非決定的な述語に適用して、入力リストの順列を列挙する関数を取得します。モデル化された非決定性のバリエーションについて議論するとともに、ソートアルゴリズムとプレイ中の述語の必要なプロパティの最下部に到達します。

さらに、どのソート関数を使用しても、対応する置換関数は入力リストのすべての置換を列挙するという定理を定式化し、証明します。関数の型のみから派生した自由定理を使用して、ステートメントを証明します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.