概要
配列のソートアルゴリズムの1つ。
アルゴリズム
- 変数定義
- $N$:配列の長さ
- $a(i)$:対象の配列の $i$ 番目の要素。$i = 1,2,\cdots,N$
- $i_\mathrm{max}$:そのループで調べるインデックスの最大値。$N-1$ で初期化
- $i = 1,2,\cdots,i_\mathrm{max}$ の順に以下の操作を行う
- もし $a(i) \gt a(i+1)$ なら、$a(i)$ と $a(i+1)$ の値を入れ替え
- $i_\mathrm{max}$ の値を1小さくする
- $i_\mathrm{max} = 0$ となるまで2,3 を繰り返す
【NOTE】ループごとに $i_\mathrm{max}$ を小さくする理由
2は隣り合う左右を比較して、大きい方の値を左から右へ順番に移動させていく操作
→ 2の操作が終わった時点で、$a(1),a(2),\cdots,a(i_\mathrm{max}),a(i_\mathrm{max}+1)$ の中で $a(i_\mathrm{max}+1)$ が最大であることが保証できる。
→ 必ず $a(i_\mathrm{max}) \lt a(i_\mathrm{max}+1)$ なので、次のループでは $a(i_\mathrm{max})$ と $a(i_\mathrm{max}+1)$ を比べる必要がない。
具体例
各計算ステップにおいて、
- 注目している部分を
(4 5)
- 順序が確定したものを
<5>
のように表す。
(4 3) 5 1 2
左の方が大きいので入れ替え
3 (4 5) 1 2
左の方が小さいのでそのまま
3 4 (5 1) 2
左の方が大きいので入れ替え
3 4 1 (5 2)
左の方が大きいので入れ替え --> 5の位置が確定
(3 4) 1 2 <5>
左の方が小さいのでそのまま
3 (4 1) 2 <5>
左の方が大きいので入れ替え
3 1 (4 2) <5>
左の方が大きいので入れ替え --> 4の位置が確定
(3 1) 2 <4> <5>
左の方が大きいので入れ替え
1 (3 2) <4> <5>
左の方が大きいので入れ替え --> 3の位置が確定
(1 2) <3> <4> <5>
左の方が小さいのでそのまま --> 1,2の位置が確定
<1> <2> <3> <4> <5>
ソート完了
計算量
時間計算量
配列サイズ $N$ に対するループごとの比較回数は
- 1ループ目:$i=1,\cdots,N-1$ の $N-1$ 回
- 2ループ目:$i=1,\cdots,N-2$ の $N-2$ 回
- …
- $N-1$ ループ目:$i=1$ の1回
であるから、合計の比較回数は
\[\begin{eqnarray} \sum_{k=1}^{N-1} (N-k) &=& \sum_{k=1}^{N-1} N - \sum_{k=1}^{N-1} k \\ &=& N(N-1) - \cfrac{1}{2}(N-1)N \\ &=& \cfrac{1}{2}(N-1)N \end{eqnarray}\]したがって時間計算量は $O(N^2)$
空間計算量
元の配列の中身を入れ替えるだけなので、元の配列分のメモリしか消費しない。 したがって、空間計算量は $O(N)$
実装
テスト:
>>> test_sort_algorithm(BubbleSort())
OK: [1, 6, 2, 7, 5, 4, 3] --> [1, 2, 3, 4, 5, 6, 7]
OK: [2, 4, 3, 7, 6, 5, 1] --> [1, 2, 3, 4, 5, 6, 7]
OK: [3, 1, 5, 7, 6, 4, 2] --> [1, 2, 3, 4, 5, 6, 7]
OK: [3, 6, 5, 7, 4, 2, 1] --> [1, 2, 3, 4, 5, 6, 7]
OK: [4, 3, 7, 6, 5, 2, 1] --> [1, 2, 3, 4, 5, 6, 7]
OK: [5, 2, 1, 7, 6, 4, 3] --> [1, 2, 3, 4, 5, 6, 7]
OK: [5, 7, 2, 6, 4, 3, 1] --> [1, 2, 3, 4, 5, 6, 7]
OK: [6, 4, 3, 7, 5, 2, 1] --> [1, 2, 3, 4, 5, 6, 7]
OK: [7, 2, 5, 6, 4, 3, 1] --> [1, 2, 3, 4, 5, 6, 7]
OK: [7, 6, 5, 4, 3, 2, 1] --> [1, 2, 3, 4, 5, 6, 7]
All 5040 tests passed.
(参考)test_sort_algorithm
:指定した長さの全ての組み合わせの配列を生成してソート結果をテストする関数
時間計算量 $O(n^2)$ の確認:
Ns = np.arange(1, 20+1) * 1000
ave, std = experiment_computing_time(BubbleSort(), Ns)
draw_computing_order(Ns, ave, std)