|
4 | 4 |
|
5 | 5 | - 隣り合う要素の大小を比較しながら整列させる。 |
6 | 6 | - アルゴリズムが単純で実装が用意なため、並列処理との親和性が高い。 |
7 | | -- バブルソートを 1 回行うと最後の要素が最大になるため、次回のスキャン範囲を 1 狭めることができる。 |
8 | | -- そのため、平均計算時間は、n-1+n-2...1=n(n-1)/2 である。 |
9 | | -- よって計算量は O(n^2) |
10 | | -- 最後に要素の交換を行った位置から、スキャン範囲の最後の要素にたどり着くまでの間に連続して要素の交換が発生しない場合にはスキャン回数を大幅に狭めることができる。--> つまり、ソート済みの部分があると実行速度が上がる。 |
| 7 | +- バブルソートを 1 回行うと右端(もしくは左端)の要素がソートされるため、次回のスキャン範囲を 1 狭めることができる。 |
| 8 | +- 最良ケース、平均ケース、最悪ケースともにO(n^2) (交換回数ではなく、比較回数) |
| 9 | + - n - 1 + n - 2 ... 1 = n(n - 1) / 2 = (n^2 - n) / 2 |
| 10 | + |
| 11 | +### 実装例 |
| 12 | + |
| 13 | +- [x] C |
| 14 | +- [x] Python |
| 15 | +- [x] Haskell |
11 | 16 |
|
12 | 17 | --- |
13 | 18 |
|
|
21 | 26 | - 最悪ケース(逆順に整列されている場合)はO(n^2) |
22 | 27 | - i番目の挿入で毎回右端まで移動するので、1 + 2 + 3 ... + (n-1) = n(n-1)/2 回の比較が必要になる。 |
23 | 28 |
|
| 29 | +### 実装例 |
| 30 | + |
| 31 | +- [x] C |
| 32 | +- [x] Python |
| 33 | +- [x] Haskell |
| 34 | + |
24 | 35 | --- |
25 | 36 |
|
26 | 37 | ## 選択ソート(Selection Sort) |
|
30 | 41 | - リストサイズを n とすると、1 回目は n 回の探索、2 回目は n-1 回の探索が必要になる。そのため、ステップ数は n + (n-1) + (n -2) ... 1 = n(n+1)/2 となる。 |
31 | 42 | - よって計算量は O(n^2)となる。 |
32 | 43 |
|
| 44 | +### 実装例 |
| 45 | + |
| 46 | +- [x] C |
| 47 | +- [x] Python |
| 48 | +- [x] Haskell |
| 49 | + |
33 | 50 | --- |
34 | 51 |
|
35 | 52 | ## マージゾート(Merge Sort) |
36 | 53 |
|
37 | | -- 2 つのソート済みの配列をつなげてソートする方法。配列を 1 個ずつに分解して,マージする過程で順番を並べることを繰り返す。 |
38 | | -- そのため,まず配列をサイズ 1 まで小さくしてそれを結合する過程でマージする操作を繰り返す。 |
39 | | -- 問題を細かく分割し、簡単な問題を解くことを積み重ねて全体の回を得るマージソートのような問題を分割統治法(divide-and-conquer method)と呼ぶ。 |
| 54 | +- 2 つのソート済みの配列をつなげてソートする方法。配列をサイズ1まで一度分解し,マージする過程で順番を並べることを繰り返す。 |
| 55 | +- 最良ケース、平均ケース、最悪ケースともにO(nlog2 n)である。 |
| 56 | + |
| 57 | +> [!NOTE] |
| 58 | +> 問題を細かく分割し、簡単な問題を解くことを積み重ねて全体の回を得るマージソートのような問題を分割統治法(divide-and-conquer method)と呼ぶ。 |
40 | 59 |
|
41 | 60 | ### マージソートの計算量 |
42 | 61 |
|
|
54 | 73 | - この木の高さ log2 n に対して、2 分木の高さを半分にするには,おおよそ n/2 回のマージが必用である。 |
55 | 74 | - このことからマージソートの計算量は O(nlog2 n) であることがわかる。 |
56 | 75 |
|
57 | | -### スターリングの近似 |
| 76 | +#### スターリングの近似 |
58 | 77 |
|
59 | 78 | > [スターリングの近似](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%AE%E8%BF%91%E4%BC%BC) |
60 | 79 |
|
|
65 | 84 | - つまり,2^k >= (n/e)^n \* √(2πn) である。 |
66 | 85 | - これを変形して,k >= nlog2 n - nlog2 e + 1/2log2(2πn) であるためマージソートの計算量は理論上最小であることがわかる。 |
67 | 86 |
|
| 87 | +### 実装例 |
| 88 | + |
| 89 | +- [x] C |
| 90 | +- [x] Python |
| 91 | +- [x] Haskell |
| 92 | + |
68 | 93 | --- |
69 | 94 |
|
70 | 95 | ## クイックソート(Quick Sort) |
71 | 96 |
|
72 | 97 | - マージソートと同じ分割統治法(divide-and-conquer method)に分類される。 |
73 | 98 | - 配列から pivot を選択し、pivot より小さいか大きいかで配列を 2 つに分割し,分割した配列それぞれに対してこの操作を繰り返していくことでソートされる。 |
74 | | -- クイックソートは pivot からソート完了後の位置に配置されていく。 |
75 | | -- 計算量は O(nlog2 n) |
76 | | - > [!NOTE] |
77 | | - > クイックソートは高速だが,ピボットの位置を固定している場合にパフォーマンスが落ちてしまうことがある。これは,ピボットで配列を 2 つに分割した時に片方に配列のサイズが大きくなってしまった場合にパフォーマンスが悪くなる。 |
78 | | - > つまり,ソート済みの配列に対しては毎回分割が発生するため,1 + 2 + 3 ... n-1 = (n)(n-1)/2 となるため最悪計算量は O(n^2)である。 |
| 99 | +- 計算量は最良ケースでは、O(nlog2 n) |
| 100 | +- 平均ケースでも、O(nlog2 n) |
| 101 | +- 最悪ケースでは、O(n^2) |
| 102 | + |
| 103 | +> [!NOTE] |
| 104 | +> クイックソートは高速だが,ピボットの位置を固定している場合にパフォーマンスが落ちてしまうことがある。 |
| 105 | +> これは,ピボットで配列を 2 つに分割した時に片方に配列のサイズが大きくなってしまった場合にパフォーマンスが悪くなる。 |
| 106 | +> 左端をピボッドにするとした場合には,ソート済みの配列に対しては毎回分割が発生するため,1 + 2 + 3 ... n-1 = (n)(n-1)/2 となるため最悪計算量は O(n^2)である。 |
| 107 | +
|
79 | 108 | - そこで pivot をランダムに選択するようにすることで、パフォーマンスが劇的に低下することを防ぐことができる。 |
80 | 109 | - このようにアルゴリズムの実行にランダムな要素を含むものを乱択アルゴリズムという。 |
81 | 110 |
|
| 111 | +### 実装例 |
| 112 | + |
| 113 | +- [x] C |
| 114 | +- [x] Python |
| 115 | +- [x] Haskell |
| 116 | + |
82 | 117 | --- |
83 | 118 |
|
84 | 119 | ## シェルソート(Shell Sort) |
85 | 120 |
|
86 | 121 | - 挿入ソートの改良版。 |
87 | | -- クイックソートが発見されるまでは最も高速なソートアルゴリズムだった。 |
| 122 | +- クイックソートが発見されるまでは最も高速なソートアルゴリズムだったらしい。 |
88 | 123 | - 挿入ソートがある程度整列済みのデータに関して高速であることを利用して予め整列度を高めるための前処理を行う。 |
89 | 124 | - h-ソート: 前処理として一定の距離(h)離れた要素を比較し,ソートする。 |
90 | | -- この例では h を 3n+1 としてループを回す毎に h を 3 で割る(切り捨て)しつつ,挿入ソートと同様に 2 つの値を比較して並べることを繰り返す。 |
| 125 | +- この例ではKnuthの間隔列を用いる。 |
| 126 | + |
| 127 | +### 実装例 |
| 128 | + |
| 129 | +- [x] C |
| 130 | +- [x] Python |
| 131 | +- [x] Haskell |
91 | 132 |
|
92 | 133 | --- |
93 | 134 |
|
|
96 | 137 | - 木構造の一種であるヒープというデータ構造を用いて,データを並び替えるソート方法。 |
97 | 138 | - ヒープとは: 木のデータ構造のうち,どの親子も親>=子,あるいは親<=子の関係にあること。 |
98 | 139 |
|
| 140 | +### 実装例 |
| 141 | + |
| 142 | +- [x] C |
| 143 | +- [x] Python |
| 144 | +- [] Haskell |
| 145 | + |
99 | 146 | --- |
| 147 | + |
| 148 | +## 計数ソート(Counting Sort) |
0 commit comments