2010-11-30 5 views
0

私はクラスのプロジェクトに取り組んでいます。指定された値の挿入ソートに移行するクイックソートを記述する必要があります。私が今では難しかった問題は、なぜ私が期待しているパフォーマンスを得ていないのかを理解することです。挿入のあるクイックソートソートフィニッシュ - どこが間違っていますか?

要件の1つは、1300ミリ秒未満で5,00,000の整数の配列をソートする必要があることです(これは標準的なマシン上にあるため、CPUの速度は問題になりません)。まず、スタックオーバーフローエラー(再帰呼び出しが多すぎます)のために、5,000,000で動作するようにはできません。私がヒープサイズを大きくすると、私はまだそれよりもかなり遅くなっています。

以下はコードです。誰のヒント?事前に

おかげ

public class MyQuickSort { 

    public static void sort(int [] toSort, int moveToInsertion) 
    { 
     sort(toSort, 0, toSort.length - 1, moveToInsertion); 
    } 

    private static void sort(int[] toSort, int first, int last, int moveToInsertion) 
    { 
     if (first < last) 
     { 
      if ((last - first) < moveToInsertion) 
      { 
       insertionSort(toSort, first, last); 
      } 
      else 
      { 
       int split = quickHelper(toSort, first, last); 
       sort(toSort, first, split - 1, moveToInsertion); 
       sort(toSort, split + 1, last, moveToInsertion); 
      } 
     } 
    } 

    private static int quickHelper(int[] toSort, int first, int last) 
    { 
     sortPivot(toSort, first, last); 
     swap(toSort, first, first + (last - first)/2); 
     int left = first; 
     int right = last; 
     int pivotVal = toSort[first]; 
     do 
     { 
      while ((left < last) && (toSort[left] <= pivotVal)) 
      { 
       left++; 
      } 

      while (toSort[right] > pivotVal) 
      { 
       right--; 
      } 

      if (left < right) 
      { 
       swap(toSort, left, right); 
      } 

     } while (left < right); 

     swap(toSort, first, right); 


     return right; 
    } 

    private static void sortPivot(int[] toSort, int first, int last) 
    { 
     int middle = first + (last - first)/2; 

     if (toSort[middle] < toSort[first]) swap(toSort, first, middle); 

     if (toSort[last] < toSort[middle]) swap(toSort, middle, last); 

     if (toSort[middle] < toSort[first]) swap(toSort, first, middle); 

    } 

    private static void insertionSort(int [] toSort, int first, int last) 
    { 
     for (int nextVal = first + 1; nextVal <= last; nextVal++) 
      { 
       int toInsert = toSort[nextVal]; 
       int j = nextVal - 1; 
       while (j >= 0 && toInsert < toSort[j]) 
       { 
        toSort[j + 1] = toSort[j]; 
        j--; 
       } 
       toSort[j + 1] = toInsert; 
      } 
    } 

    private static void swap(int[] toSort, int i, int j) 
    { 
     int temp = toSort[i]; 
     toSort[i] = toSort[j]; 
     toSort[j] = temp; 
    } 

} 
+0

これはどのクラスですか?高校/カレッジ?私は好奇心が強いです –

+0

カレッジ、2年生、私は何か複雑なことを想像しません...私はちょっとしたエラーがあるかもしれないと思います... – addisonj

+0

'moveToInsertion'とは何ですか? – helpermethod

答えて

0

それを実演しました。

実際には、私の分類はまったく間違っていません。私は0〜100の範囲の数値を生成していました(ソートされているかどうかをテストするため)。その結果、多数の重複が発生し、多くのパーティションに影響を及ぼしていました。範囲をmin_intとmax_intに変更すると、それはずっと速くなりました。

ありがとうございました:D

+0

はい、これはクイックソートで気を付ける別の落とし穴です。あなたはこれを処理できるようにBentley-McIlroyのパーティション分割(google it)を使うことができます。 –

-1

入力配列は、再帰関数は、スタックオーバーフローの問題に実行することを期待するのは、大規模なその自然です。これは上記のコードを試してみるとここで起こっていることです。独自のスタックを使用してQuicksortを繰り返し書くことをお勧めします。実行時にスタックフレームの割り当て/解放が行われないため、高速にする必要があります。スタックオーバーフローの問題も発生しません。パフォーマンスは、挿入ポイントを実行しているポイントによっても異なります。私は、挿入ソートがクイックソートに比べてひどく実行される特定の入力サイズは持っていません。サイズを変えて試してみることをお勧めします。

パフォーマンスを向上させるために、挿入ソートでバイナリ検索を使用することもできます。私はあなたが小さな入力を実行するときにどれだけ改善するのか分かりませんが、それは素晴らしいトリックです。

再帰クイックソートを反復クイックソートに変換する方法を学ぶことができないため、コードを共有したくありません。反復的なものに変換する際に問題がある場合は、私に知らせてください。

+0

@Srikanth - 再帰から反復への変換はこの人を助けません。大規模なデータセットは代わりにOutOfMemoryErrorを生成します。 –

+0

バイナリの挿入の並べ替えここでの悪い考えです...バイナリの挿入の並べ替えは、標準の挿入の並べ替え(数字は異なる場合があります)よりも配列が100以上の場合に高速ですが、ここで挿入の並べ替えは、より少ない、例えば10-12。 – helpermethod

+0

@Amir:もちろん、与えられた解は、入力が十分に大きい場合、OutOfMemoryErrorに実行できます。再帰から反復への変換は、プログラムが処理できる入力サイズを増加させます。関数calスタックが占有する領域は、明らかに反復型で使用されるスタックよりも多い。つまり、再帰バージョンでnの入力サイズを処理できる場合、同じメモリを持つ反復バージョンではnより大きい入力を処理できます。私は、関数呼び出しスタックによる正確なスペース使用量を知らなければ、その上限を言うことはできません。私は大規模なデータセット*として5,00,000のintsを考慮しません。 – Srikanth

0

これはあなたのアルゴリズムではテストされていませんが、実行しているデータセットの種類はわかりませんが、左端の要素よりも優れたピボットを選択することを検討してください。クイックソートのウィキペディアから:ピボットの

選択肢クイックソートの非常に初期のバージョン では、 の左端の要素パーティションは、多くの場合、 ピボット要素として選択されます。残念ながら、この は、すでに ソートされた配列で最悪の動作を引き起こします。これはむしろ の一般的な使用例です。この問題は 容易(特により長いパーティションに) 中間パーティションのインデックスまたは を選択し、ピボットのいずれかのため ランダムインデックスを選択することによって解決された パーティションの最初、 中間及び最後の要素の中央値を選択しますピボットのために

+1

私のコードを詳しく見てみると、(sortPivotの下の)最初、中央、最後の中央値を探していることが分かります。私は最初の位置に移動し、それを私のピボットとして使用します(私が知っていることの面白い方法、それはちょうど速かった:P) – addisonj

+0

なぜそれらのフープをすべて通過するのですか?配列の中央で値を選択するだけではどうですか?スワップを行うメソッドへの呼び出しは不要です。 –

関連する問題