クイックソートアルゴリズムの実装と最適化

クイックソートアルゴリズムの実装と最適化

[[385051]]

この記事はWeChatの公開アカウント「Beta Learns JAVA」から転載したもので、著者はSilently9527です。この記事を転載する場合は、Beta Learning JAVA パブリック アカウントにお問い合わせください。

この記事は Github リポジトリ https://github.com/silently9527/JavaCore に含まれています。

プログラマーがよく使用する IDEA プラグイン: https://github.com/silently9527/ToolsetIdeaPlugin

完全にオープンソースの Taobao プロジェクト: https://github.com/silently9527/mall-coupons-server

序文

クイック ソートは、最も広く使用されているソート アルゴリズムと言えます。その主な特徴は、インプレース ソート (補助配列が不要で、スペースを節約) に基づいていることです。実際、長さ N の配列にクイック ソートを使用する場合の時間計算量は NlogN です。以前の記事では他のソート アルゴリズムについても説明しましたが、いずれもこれら 2 つの機能を組み合わせることができませんでした。

簡単な並べ替えのアイデア

クイックソートも分割統治ソートアルゴリズムであり、配列を 2 つのサブ配列に分割し、サブ配列を再帰的にソートして、最終的に配列全体が整列していることを保証します。

アルゴリズムのアイデア:

  1. 分割要素(通常は配列の最初の要素)をランダムに選択します。
  2. 配列の左側からスキャンして分割要素以上の値を見つけ、配列の右側からスキャンして分割要素以下の値を見つけ、2つの値を交換します。
  3. このプロセスは、左と右のポインタが出会うまで繰り返され、分割された要素の左側の値がその値よりも小さく、右側の要素がその値よりも大きくなるように要素が配置されます。
  4. このプロセスは再帰的に繰り返され、配列全体が正しい順序になっていることが保証されます。

アルゴリズムの実装

クイックソートアルゴリズムの考え方に従って、実装の最初のバージョンを記述できます。

  1. パブリッククラス QuickSort は SortTemplate を実装します {
  2. @オーバーライド
  3. パブリックvoid ソート(比較可能な[] 配列) {
  4. クイックソート(配列、0、配列の長さ - 1);
  5. }
  6.  
  7. プライベートvoid quickSort(Comparable[]配列、 int lo、 int hi) {
  8. (最低 >= 最高)の場合{
  9. 戻る;
  10. }
  11. intパーティション = パーティション(配列、lo、hi);
  12. quickSort(配列、lo、パーティション - 1);
  13. クイックソート(配列、パーティション + 1、hi);
  14. }
  15.  
  16. プライベートintパーティション(比較可能な[]配列、 int lo、 int hi) {
  17. int i = lo、j = hi + 1;
  18. 比較可能なel = array[lo];
  19. )の間{
  20. while (less(配列[++i], el)) {
  21. もし(i == hi){
  22. 壊す;
  23. }
  24. }
  25. while (less(el, 配列[ --j])) {  
  26. もし(j == lo){
  27. 壊す;
  28. }
  29. }
  30. もし (i >= j) {
  31. 壊す;
  32. }
  33. exch(配列、i、j);
  34. }
  35. exch(配列, lo, j);
  36. jを返します
  37. }
  38. }

このコードはクイックソートの従来の実装です。最悪のケースとして、ソートする配列がすでに [1,2,3,4,5,6,7,8] の順序になっている場合、クイックソートを実行するプロセスは図のようになります。

長さ N の配列の場合、最悪の場合、再帰が N-1 回必要になるため、時間の計算量は O(n2) になります。この状況を回避するために、アルゴリズムを改善する方法を見てみましょう。

アルゴリズムの改善

  • ランダム性を確保し、最悪の状況を回避するには、2 つの方法があります。1 つ目は、配列を並べ替える前にランダムにシャッフルすることです。2 つ目は、パーティション メソッドで分割要素を最初の要素ではなくランダムに選択することです。簡単な実装:
  1. プライベートintパーティション(比較可能な[]配列、 int lo、 int hi) {
  2. int i = lo、j = hi + 1;
  3. int random = new Random().nextInt(hi - lo) + lo;
  4. exch(配列、lo、ランダム);
  5. 比較可能なel = array[lo];
  6. )の間{
  7. while (less(配列[++i], el)) {
  8. もし(i == hi){
  9. 壊す;
  10. }
  11. }
  12. while (less(el, 配列[ --j])) {  
  13. もし(j == lo){
  14. 壊す;
  15. }
  16. }
  17. もし (i >= j) {
  18. 壊す;
  19. }
  20. exch(配列、i、j);
  21. }
  22. exch(配列, lo, j);
  23. jを返します
  24. }
  • 挿入ソートへの切り替えはマージソートと同じです。小さな配列の場合は、直接挿入ソートに切り替えます。
  1. プライベートvoid quickSort(Comparable[]配列、 int lo、 int hi) {
  2. (最低 >= 最高)の場合{
  3. 戻る;
  4. }
  5.      
  6. if (hi - lo < 5) { //テスト、5未満の場合、挿入ソートに切り替える
  7. 挿入ソート(配列、lo、hi);
  8. 戻る;
  9. }
  10.  
  11. intパーティション = パーティション(配列、lo、hi);
  12. quickSort(配列、lo、パーティション - 1);
  13. クイックソート(配列、パーティション + 1、hi);
  14. }
  15.  
  16. // ソートを挿入
  17. プライベートvoid挿入ソート(比較可能な配列[]、 int lo、 int hi) {
  18. ( int i = lo; i <= hi; i++) {
  19. for ( int j = i; j > lo && less(array[j], array[j - 1]); j --) {  
  20. exch(配列、j、j - 1);
  21. }
  22. }
  23. }

3 方向の分割 ソートする必要がある配列内に多数の繰り返し要素がある場合、実装するクイック ソートでは、再帰中に完全に繰り返されるサブ配列が多数発生します。それでもアルゴリズムでは分割されるため、改善の余地が大いにあります。

アイデアは、まず分割要素 (el) をランダムに選択し、次に配列を 3 つの部分 (より大きい、等しい、より小さい) に切り替えることです。1 回の再帰で、分割要素に等しいすべての値をソートできます。ポインタ lt と gt を維持して、a[lo..lt-1] が分割要素より小さく、a[gt+1..hi] が分割要素より大きくなるようにします。

  • 変数を初期化します: lt=lo、i=lo+1、gt=hi
  • a[i] < el の場合、a[i] と a[lt]、i++、lt++ を入れ替える
  • a[i] > el の場合、a[gt] と a[i] を入れ替え、gt--
  • a[i] == el; i++

コード実装:

  1. パブリッククラス Quick3waySort は SortTemplate を実装します {
  2. @オーバーライド
  3. パブリックvoid ソート(比較可能な[] 配列) {
  4. クイックソート(配列、0、配列の長さ - 1);
  5. }
  6.  
  7. @SuppressWarnings( "チェックなし" )
  8. プライベートvoid quickSort(Comparable[]配列、 int lo、 int hi) {
  9. (最低 >= 最高)の場合{
  10. 戻る;
  11. }
  12. int lt = lo、i = lo + 1、gt = hi;
  13. 比較可能なel = array[lo];
  14. (i <= gt) の間 {
  15. int tmp = el.compareTo(配列[i]);
  16. (tmp > 0)の場合{
  17. exch(配列、lt++、i++);
  18. }それ以外の場合 (tmp < 0) {
  19. exch(配列、i、gt --);  
  20. }それ以外{
  21. 私は++;
  22. }
  23. }
  24. クイックソート(配列、lo、lt - 1);
  25. クイックソート(配列、gt + 1、hi);
  26. }
  27. }

<<:  連休明けの電力安定供給のため、変電所点検ロボットが活躍中

>>:  機械学習で保険ビジネスの問題を簡素化する3つのシナリオ

ブログ    
ブログ    
ブログ    

推薦する

AIを使って人間の子どもを「飼い慣らす」: ハードコアな子育ての楽しさを発見した父親

技術オタクの父親たちは、Netflix のエピソードを数本静かに観るために何をするのでしょうか? [...

国産初のオープンソースMoE大型モデルが登場!パフォーマンスはLlama 2-7Bに匹敵し、計算量は60%削減されます。

オープンソースのMoEモデルがついに国内初のプレイヤーを迎えます!そのパフォーマンスは高密度の Ll...

Google ドキュメントでテキスト要約を自動的に生成できるようになりました。

私たちの多くは、毎日たくさんのファイルを処理する必要があります。新しい文書を受け取ったとき、通常は、...

「顔をスキャン」すると、実はリスクが伴う。顔認識、個人情報保護の観点から

[[417904]]例:2020年6月、杭州市阜陽区人民法院は、郭兵と杭州野生動物公園との間のサービ...

...

AI がデータセンターを持続可能性の原動力に変える方法

これまで多くの技術進歩の基盤となってきたデータセンターは、現在、インフラストラクチャ プロバイダーだ...

ディープ CNN アーキテクチャの革新の 7 つのカテゴリのレビュー

ディープ畳み込みニューラル ネットワーク (CNN) は、さまざまな競合ベンチマークで最先端の結果を...

AIが人間の翻訳者に取って代わり、有名なテクノロジーウェブサイトは13歳の従業員まで解雇し、ウェブサイトはすべてAIによって制作されている

AIは本当に人間の仕事を奪う——有名なテクノロジーウェブサイト「ギズモード」が、スペイン語チャンネル...

RLHF が LLM トレーニングの鍵となるのはなぜですか? AI専門家が5つの代替案を検討し、Llama 2のフィードバックメカニズムのアップグレードを説明

ChatGPTが主導する大規模言語モデルの時代において、避けては通れないトピックが「人間のフィードバ...

ビッグデータと人工知能の関係

[[342758]]人工知能教育は最も美しい新しいインフラです人工知能のアルゴリズムの中にはデータ...

...

1 週間で機械学習を始めることは信頼できるでしょうか?詳しい学習スケジュールはこちら

[[185648]]原著者 | ペル・ハラルド・ボルゲン編集:魏子民、頼暁娟、張立軍 「初心者にとっ...

AIが伝染病と闘う: 時折の恥ずかしさの裏に究極の防壁が現れる

人類と新型コロナウイルスとの戦いは今も続いていますが、この間、さまざまな「人工知能+」アプリケーショ...