naoのブログ ~エンジニアスキルに生成演算子を作用させたい~

日々アウトプットしていくことを目標に、だらだらと書いていきます。

アルゴリズムの基礎: クイックソート

クイックソート

フローチャートは、以下の本を参考に作成しました。

丁寧な解説は上記の本など参考してもらうとして、 クイックソートの基本方針は以下のとおりです。

  • 手前から基準値よりも大きい値をさがす
  • 後ろから基準値よりも小さい値をさがす
  • それらを交換する

フローチャート

上記の基本方針をもとに、実行されています。 また、最後に基準値を正しい場所に移動する処理も追加しています。

f:id:nao_000:20190630170954p:plain

以下、上記のフローチャートpythonで実行した例です。

import random

N = 9
# get random array
arr = list(range(1, N+1))
random.shuffle(arr)
print("inital array: {0}".format(arr))

# initial value
left = 0
right = N -1

def quick_sort(arr, left, right):
  i = left + 1
  k = right
  _ref_value = arr[left] # reference value
  _tmp = None
  print("start: left={0}, right={1}, arr[left:right+1]={2}".format(left, right, arr[left:right+1]))
  print("\t _ref_value={0}".format(_ref_value))

  while (i < k):
    # looking for a value bigger than _ref_value
    while (arr[i] < _ref_value and i < right):
      i = i + 1
    # looking for a value smaller than _ref_value
    while (arr[k] >= _ref_value and left < k):
      k = k - 1

    if (i < k):
      print("\t i={0}, arr[i]={1}, k={2}, arr[k]={3}".format(i, arr[i], k, arr[k]))
      _tmp = arr[i]
      arr[i] = arr[k]
      arr[k] = _tmp

  # replace refrence value
  if (arr[left] > arr[k]):
    _tmp = arr[left]
    arr[left] = arr[k]
    arr[k] = _tmp
    print("\t left={0}, arr[left] = {1}, k={2}, arr[k]={3}".format(left, arr[left], k, arr[k]))

  print("\t result: {0}".format(arr))

quick_sort(arr, left, right)

以下、実行例です。 実行すると、基準値5を境にして、[3,4,1,2], [5], [7,8,9,6]のようにソートされます。

inital array: [5, 4, 8, 7, 3, 2, 1, 9, 6]
start: left=0, right=8, arr[left:right+1]=[5, 4, 8, 7, 3, 2, 1, 9, 6]
   _ref_value=5
   i=2, arr[i]=8, k=6, arr[k]=1
   i=3, arr[i]=7, k=5, arr[k]=2
   left=0, arr[left] = 3, k=4, arr[k]=5
   result: [3, 4, 1, 2, 5, 7, 8, 9, 6]

最後にarr[k]に基準値を入れています。 そのため、以下の図のようになっています。

  • [left, k-1]は基準値よりも小さい値の配列
  • kの位置には実行時に決定された基準値
  • [k+1, right]は基準値よりも大きい値の配列

しかし、基準値以外は正しく順番にソートされていません。 残りの部分に対して、正しくソートするために、再び[left, k-1]および[k+1, right]の 配列に対して、上記の操作を再帰的に行う必要があります。

f:id:nao_000:20190630171035p:plain

再帰的に実行

quick_sort()を再帰的に実行して、全体をソートします。 以下のフローチャートにて、オレンジ部分が新たに追加した箇所です。

このように再帰的に呼び出しを行うことで、配列のすべての値が正しくソートされるようになります。

f:id:nao_000:20190630171110p:plain

pythonでの実装例を示します。

import random

N = 9
# get random array
arr = list(range(1, N+1))
random.shuffle(arr)
print("inital array: {0}".format(arr))

# initial value
left = 0
right = N -1

def quick_sort(arr, left, right):
  i = left + 1
  k = right
  _ref_value = arr[left] # reference value
  _tmp = None
  print("start: left={0}, right={1}, arr[left:right+1]={2}".format(left, right, arr[left:right+1]))
  print("\t _ref_value={0}".format(_ref_value))

  while (i < k):
    # looking for a value bigger than _ref_value
    while (arr[i] < _ref_value and i < right):
      i = i + 1
    # looking for a value smaller than _ref_value
    while (arr[k] >= _ref_value and left < k):
      k = k - 1

    if (i < k):
      print("\t i={0}, arr[i]={1}, k={2}, arr[k]={3}".format(i, arr[i], k, arr[k]))
      _tmp = arr[i]
      arr[i] = arr[k]
      arr[k] = _tmp

  # replace refrence value
  if (arr[left] > arr[k]):
    _tmp = arr[left]
    arr[left] = arr[k]
    arr[k] = _tmp
    print("\t left={0}, arr[left] = {1}, k={2}, arr[k]={3}".format(left, arr[left], k, arr[k]))

  print("\t result: {0}".format(arr))

  # call quick_sort() recursively
  if (left < k - 1):
    quick_sort(arr, left, k - 1)
  if (k + 1 < right):
    quick_sort(arr, k + 1, right)

quick_sort(arr, left, right)

以下に実行結果の一例です。

inital array: [7, 4, 9, 2, 5, 6, 1, 3, 8]
start: left=0, right=8, arr[left:right+1]=[7, 4, 9, 2, 5, 6, 1, 3, 8]
   _ref_value=7
   i=2, arr[i]=9, k=7, arr[k]=3
   left=0, arr[left] = 1, k=6, arr[k]=7
   result: [1, 4, 3, 2, 5, 6, 7, 9, 8]
start: left=0, right=5, arr[left:right+1]=[1, 4, 3, 2, 5, 6]
   _ref_value=1
   result: [1, 4, 3, 2, 5, 6, 7, 9, 8]
start: left=1, right=5, arr[left:right+1]=[4, 3, 2, 5, 6]
   _ref_value=4
   left=1, arr[left] = 2, k=3, arr[k]=4
   result: [1, 2, 3, 4, 5, 6, 7, 9, 8]
start: left=1, right=2, arr[left:right+1]=[2, 3]
   _ref_value=2
   result: [1, 2, 3, 4, 5, 6, 7, 9, 8]
start: left=4, right=5, arr[left:right+1]=[5, 6]
   _ref_value=5
   result: [1, 2, 3, 4, 5, 6, 7, 9, 8]
start: left=7, right=8, arr[left:right+1]=[9, 8]
   _ref_value=9
   left=7, arr[left] = 8, k=8, arr[k]=9
   result: [1, 2, 3, 4, 5, 6, 7, 8, 9]

今後は、実際にpostgresqlでのクイックそーその実装部分に関して勉強して記事にしたいです。