データ構造とアルゴリズム

第六回 (2012年11月9日)

クイックソート、平均計算量

http://www.sw.it.aoyama.ac.jp/2012/DA/lecture6.html

Martin J. Dürst

duerst@it.aoyama.ac.jp

AGU

© 2008-12 Martin J. Dürst 青山学院大学

目次

前回のまとめ

レポートについて

[諸事情により削除]

今日の目的

クイックソートの歴史

(quicksort)

分割統治法の見直し

クイックソートの基本動作

Ruby による実装: 6qsort.rbconceptual_quick_sort

クイックソートの実装

  1. 最右の要素が分割要素
  2. 右から分割要素より小さい要素を検索
  3. 左から分割要素より大きい要素を検索
  4. 2. と 3. で検索した要素を交換
  5. 交換の必要がなくなるまで 2.-4. を繰返す
  6. 分割要素を分岐点に置く
  7. 左と右で再帰的に実行

Ruby による実装: 6qsort.rbsimple_quick_sort

最悪時の計算量

(worst case running time)

最善時の計算量

(best case running time)

平均の計算量

(average running time)

QA の計算

QA(n) = n + 1 + 1/n Σ1≤kn (QA(k-1)+QA(n-k))

QA(0) + ... + QA(n-2) + QA(n-1) =
= QA(n-1) + QA(n-2) + ... + QA(0)

QA(n) = n + 1 + 2/n Σ1≤kn QA(k-1)

n QA(n) = n (n + 1) + 2 Σ1≤kn QA(k-1)

(n-1) QA(n-1) = (n-1) n + 2 Σ1≤kn-1 QA(k-1)

 

QA の計算 (続き)

n QA(n) - (n-1) QA(n-1) = n (n+1) - (n-1) n + 2 QA(n-1)

n QA(n) = (n+1) QA(n-1) + 2n

QA(n)/(n+1) =
= QA(n-1)/n + 2/(n + 1) =
= QA(n-2)/(n-1) + 2/n + 2/(n+1) =
= QA(2)/3 + Σ3≤kn 2/(k+1)

QA(n)/(n+1) ≈ 2 Σ1≤kn 2/k ≈ 2∫1n 1/x dx = 2 ln n

 

QA の計算結果

QA(n) ≈ 2n ln n ≈ 1.39 n log2 n

O(n log n)

⇒ 比較の数が最善の決定木に比べ平均で約 1.39 倍

分割要素の選択

クイックソートの諸問題

Ruby による実装 (三分割以外): 6qsort.rbquick_sort

 

アニメーションによる整列法の比較

アニメーションの閲覧: sort.svg

安定な整列法

O(n log n) より早い整列法

ビンソート

例: 学生番号で整列

C や Ruby での整列

C 言語の qsort 関数

void qsort(
    void *base,        // 配列のスタート
    size_t nel,        // 配列の要素数
    size_t width,      // 要素の大きさ
    int (*compar)(     // 比較関数
        const void *,
        const void *)
  );

Ruby の Array#sort

(Klass#method: クラス Klass のインスタンスメソッド method)

array.sort: <=> 演算子による整列

array.sort { |a, b| a.length <=> b.length }
{} 内はブロック (block) (「比較関数」)
(例えば文字列の) 長さで整列

Ruby の <=> 演算子

a と b の値の関係 a <=> b の戻り値
a < b -1
a = b 0
a > b +1

Ruby の Array#sort_by

array.sort_by { |a| a.length }
(例えば文字列の) 長さで整列

配列の項目ごとに値を事前計算

次回のための準備