Processing math: 100%

概要

配列のソートアルゴリズムの1つ。

アルゴリズム

ソート対象配列の長さ n の配列について、i 番目の要素を ai で表す(i=0,1,2,,n1)。

  1. i1
  2. j0
  3. ループ:j=i となるまで以下の操作を繰り返す
    1. aiaj であれば j 番目の位置に ai を挿入し、aj,aj+1,,ai1 を一つずつ後ろにシフトしてループを抜ける
    2. jj+1
  4. ii+1
  5. in なら2へ戻る。i>n ならソート終了

具体例

[ 4   1   5   3   2 ]
 <->  ^i=1
 sorted

先頭から i 番目までの(ソート済み)部分配列の要素を順番に見ていく。
初めて見つかる a_i=1 以上の値は a_0=4 なので、a_0 の手前に a_i を挿入して i をインクリメント: i <- i+1

[ 1   4   5   3   2 ]
 <----->  ^i=2
 sorted

先頭から i 番目までの(ソート済み)部分配列の要素を順番に見ていく。
a_i=5 以上の値は見つからないので挿入は行わずに i をインクリメント: i <- i+1

[ 1   4   5   3   2 ]
 <--------->  ^i=3
 sorted

先頭から i 番目までの(ソート済み)部分配列の要素を順番に見ていく。
初めて見つかる a_i=3 以上の値は a_1=4 なので、a_1 の手前に a_i を挿入して i をインクリメント: i <- i+1

[ 1   3   4   5   2 ]
 <------------->  ^i=4
 sorted

先頭から i 番目までの(ソート済み)部分配列の要素を順番に見ていく。
初めて見つかる a_i=2 以上の値は a_1=3 なので、a_1 の手前に a_i を挿入して i をインクリメント: i <- i+1

[ 1   2   3   4   5 ]
 <----------------->  ^i=5
 sorted

--> ソート完了

計算量

時間計算量

挿入する要素 ai それぞれについて、a0,a1,,ai1 を走査する必要があり、各ループで走査が必要な要素の数は以下の通り。

  • i=1a0 を走査
  • i=2a0,a1 を走査
  • i=3a0,a1,a2 を走査
  • i=na0,a1,,an1 を走査

走査すべき要素の総数は n(n+1)/2 なので、時間計算量は O(n2)

【NOTE】

a0,a1,,ai1 をすべて走査せずとも、途中で aiaj となる j が見つかれば、それ以降の要素を捜査する必要はなくなる。
しかし、結局はその後 aiaj の前に挿入するために玉突きのように aj,aj+1,,ai1 を1つずつ後ろに動かす作業が必要になるので、やはり計算回数は変わらない。

空間計算量

元の配列自体の要素を入れ替える方式なので、追加のメモリを必要としない。
したがって空間計算量は O(n)

実装

import numpy as np
class InsertionSort:
def sort(self, array):
a = array.copy()
n = len(a)
for i in range(1, n):
for j in range(0, i):
if a[i] <= a[j]:
tmp = a[i]
a[j+1:i+1] = a[j:i]
a[j] = tmp
return a

テスト:

>>> test_sort_algorithm(InsertionSort())
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(n2) の確認:

Ns = np.arange(1, 10+1) * 1000
ave, std = experiment_computing_time(InsertionSort(), Ns)
draw_computing_order(Ns, ave, std)

insertion-sort