データ構造とアルゴリズム
第六回 (2014年10月
24日)
分割統治法、マージソート
http://www.sw.it.aoyama.ac.jp/2014/DA/lecture6.html
Martin J. Dürst
© 2009-14 Martin
J. Dürst 青山学院大学
目次
- これからの予定
- 前回の残り・まとめ・宿題
- 整列の重要性
- 単純な整列法:
バブル整列法、選択整列法、挿入整列法
- Ruby の繰返し
- 分割統治法
- マージソート
- まとめ
これからの予定
- 10月31日: 青山祭で休業
- 11月 7日: 通常の授業
前回の残り
前回のまとめ
- 順位キューは大事な抽象データ型
- 配列や連結リストによる実装は非効率的
- ヒープでは最優先の項目が完全二分木の根に出現
- ヒープの普遍条件は heapify_up と heapify_down で保持
- ヒープソートによって整列が可能
- 完全二分木とヒープソートは配列で実装可能
前回からの宿題
- ヒープの合併を実装しなさい
- ヒープの作成の計算量について考えましょう:
heapify_all
は O(n/2 · log
n) で O(n log n)
に見えるが、細かく分析すると違う結果になる
- 情報テクノロジーでの整列 (sort)
の応用を五つ考えなさい
前回からの宿題: heapify_all
の計算量
heapify_all
の概念: 下の層から
heapify_down
を適用
heapify_down
の計算量: 最大 O(log
n)
heapify_all
は O(n log n)
と推測が、違う
- 層ごとの分析:
層 (下から) |
項目の数 |
heapify_down の計算量 |
層の計算量 |
一番目 |
n/2 |
0 (不要) |
0·n/2 |
二番目 |
n/4 |
1 |
1·n/4 |
三番目 |
n/8 |
2 |
2·n/8 |
四番目 |
n/16 |
3 |
3·n/16 |
i 番目 |
n/2i |
i-1 |
(i-1)·n/2i |
- 合計: Σ0≤i≤log n
(i-1)·n/2i =
O(n)
整列の重要性
- 出力の様々な整理
- 検索 (例: 二分探索) の前提
- アルゴリズムの部品
単純な整列方法
- バブル整列法 (bubble sort)
- 選択整列法 (selection sort)
- 挿入整列法 (insertion sort)
Ruby の様々な繰返し
一定回数の繰返し
構文:
回数.times do
# 何かの作業
end
応用例:
(length-1).times do
# bubble
end
指数を使った繰返し
構文:
出発値.upto 目的値 do |指数変数|
# 指数変数を使った作業
end
応用例:
0.upto(length-2) do |i|
# select
end
バブル整列法
(bubble sort)
- 隣接の項目を比較、順番になってないものを交換
- 配列の先頭から最後までで一つのパス
- 必要なパスの数が O(n)
- 一つのパスで比較の回数が O(n)
- 計算量は O(n2)
改良:
- 交替で両方向にバブル
- 最後の交換点を記憶、交換のチェックの範囲を限定
実装の例: 6sort.rb
(下記の整列法も同様)
選択整列法
(selection sort)
- 配列の先頭に整列済みの部分を徐々に拡大
- 整列済みでない最小の項目を繰返し検索、選択
- 整列済みの部分の直後の項目と最小の項目を交換
- 交換の回数は O(n)
- 最小の項目の割り出しは O(n)
- 比較の回数と全体の計算量は O(n2)
選択整列の計算量の詳細
- n
項目の中から最小のものを見つけるための比較回数は
n-1
- 未整列の領域の大きさは最初の n 項目から最後に 2項目
- ∑i=2n
n-i+1 = n-1 + n-2 + ... + 2 + 1
= n · (n-1) / 2 =
O(n2)
挿入整列法
(insertion sort)
- 配列の先頭に整列済みの部分を徐々に拡大
- 整列済みの部分の直後の項目を整列済みの部分に挿入
- 場所を空けるために整列済みの項目をずらす
- 計算量は (最悪で) O(n2)
- 既に殆ど整列されたデータの場合に有利
- 後から項目が追加されるときに利用可能
改良: 番兵 (sentinel) の使用:
最初の項目の前に全ての項目より小さいものを設置
挿入整列の計算量の詳細
- 挿入する項目の数が n
- i
個目の項目を挿入するには最大で比較・ずらす回数が
i-1
- ∑i=2n i-1 = 1 + 2 + ... + n-2 + n-1 =
n · (n-1) / 2 =
O(n2)
分割統治法
(divide and conquer, ラテン語: divide et impera)
- 軍事戦略や戦術の用語
- 一つの大問題を複数の小問題に分割する
ことで問題を解決する方法
- プログラミング一般の大事な原則
- アルゴリズムやデータ構造の一つの設計方針
マージソート (再帰無し)
(merge sort)
- 整列対処項目を二分割
- 両部分を別々に整列
- 両部分を併合 (merge) で統合
併合
(merge)
- 2 ウェイ併合 (two-way merge) とマルチウェイ併合 (multi-way
merge)
- 二以上の整列済みの列から一つの整列済みの列を作成
- 入力の列のどちらか小さい方の項目を次々と選択
- 最後に、残った列の残った項目を複写
マージソート
(merge sort)
- 整列対処項目を再帰的に二分割
- 項目数が 1 の場合、整列済み
- 分割した両部分を整列後に併合 (merge) で統合
マージソートの計算量
- 分割が O(1)
(配列の指数の演算のみ)
- n 項目の併合は O(n)
- 漸化式:
M(n) = 1 + 2 M(n/2) +
n (正確には 2 M(n/2)
の代わり、M(⌈n/2⌉) +
M(⌊n/2⌋))
M(1) = 0
- 繰り返しの置き換えでパターンの発見:
M(n) = 1 + 2 M(n/2) +
n =
= 1 + 2 (1+ 2 M(n/2/2) + n/2) +
n =
= 1 + 2 + 4 M(n/4) + n + n =
= 1 + 2 + 4 (1 + 2 M(n/4/2) + n/4) +
n + n =
= 1 + 2 + 4 + 8 M(n/8) + n + n
+ n =
= 2k - 1 + 2k
M(n/2k) +
kn
- M(1) = 0 を利用: n/2k = 1
⇒ k = log2 n
- M(n) = n - 1 + n
log2 n
- 漸近的計算量: O(n log n)
マージソートの特長
- 併合は複写して行うため、メモリの二倍が必要
- 内部メモリより外部メモリに最適
- 外部メモリ:
- パンチカード (punchcard)
- 磁気テープ (magnetic tape)
- ハードディスク (hard disk)
まとめ
- 単純な整列法:
- 単純な整列法は全て O(n2)
- マージソートは分割統治法に基づく
- マージソートはヒープソートと同様に
O(n log n)
レポート: 手作業によるソート
提出: 11月10日 (月曜日) 19時00分締切; O 棟
529号室の前の箱に提出; 手書き A4, 表裏で合計最大
4ページまで厳守
名前、ふりがな、学生番号だけではなく、授業名と課題名も明記のこと。
(資料を参考にしなくて解ける方がいいですが、ウェブページも含め必ず使った資料全ての詳細を列挙すること。Wikipedia
等への参照の場合、URI のではなく IRI を使用のこと (例:
http://ja.wikipedia.org/wiki/%E6%83%85%E5%A0%B1
のではなく http://ja.wikipedia.org/wiki/情報 を使用)。
採点はアルゴリズムの速さにかかわらず満点、特に早いアルゴリズムを考えた場合に追加点。
手作業の場合に効率的な順列アルゴリズムを考えて提案して分析しなさい。
条件は次の通り: 資料がそれぞれ A4
の紙一枚。見やすい位置に10桁の番号が書かれてある。その番号の昇順にソートする。番号の分布については一切不明。二つのケースを想定:
- 一人で資料6000枚をソート
- 24人で資料60000枚をソート
共通のアルゴリズムでも別々のアルゴリズムでもよい。分析では
O() での計算量 (理由つき)
と実際に想定される時間。今まで習ったアルゴリズムとの類似点や相違点、手作業の特徴を考慮した点などについても論じる。
次回のための準備
- Sorting Cards
を切り取り、授業で習ったアルゴリズムを実演
(例: 二人で選択ソートと掃流そうーとを競い合う)
- 「手作業によるソート」の検討、必要に応じて次回に質問