M.Hiroi's Home Page

お気楽 Ruby プログラミング入門

付録

[ PrevPage | R u b y | NextPage ]

ソート

いろいろなソートプログラムです。アルゴリズムの詳しい説明は拙作のページや参考文献をお読みください。

●参考文献

  1. 奥村晴彦, 『C言語による最新アルゴリズム事典』, 技術評論社, 1991
  2. 近藤嘉雪, 『Cプログラマのためのアルゴリズムとデータ構造』, ソフトバンク, 1998
  3. Motoyuki's Diary 2000/5/26(Fri)

●テストプログラム

リスト : テストプログラム

# テストデータの生成
def make_data(x)
  # x は生成するデータの個数
  # 乱数
  a = (1..x).to_a.shuffle
  # 昇順
  b = (1..x).to_a
  # 降順
  c = b.reverse
  # 山型
  d = (1..x/2).to_a + (1..x/2).to_a.reverse
  return a, b, c, d
end

# ソートのチェック
def check(buff)
  for i in 0 ... buff.size-1
    raise "sort error" if buff[i] > buff[i + 1]
  end
end

# 時間計測
def sort_test(nums, &fn)
  print "  個数 : 乱数  : 昇順  : 逆順  : 山型  \n"
  print "-------+-------+-------+-------+-------\n"
  for n in nums
    printf "%6d ", n
    for buff in make_data(n)
      s = Time.now
      fn.call(buff)
      e = Time.now
      check(buff)
      printf ": %.3f ", e - s
    end
    print "\n"
  end
end

●実行環境

Ruby 3.0, Ubunts 22.04 LTS (WSL2, Windows 10), Intel Core i5-6200U 2.30GHz

●注意事項

プログラムの実行時間は M.Hiroi のコーディング、実行したマシン、使用するプログラミング言語 (またはコンパイラ) などの環境に大きく依存しています。また、これらの環境だけではなく、データの種類によっても実行時間は大きく左右されます。興味のある方は、いろいろなデータをご自分の環境で試してみてください。

●バブルソート, シェーカーソート

リスト : ソート (1)

# バブルソート
def buble_sort(buff)
  for i in 0 ... buff.size
    j = buff.size - 1
    while j > i
      if buff[j - 1] > buff[j]
        buff[j], buff[j - 1] = buff[j - 1], buff[j]
      end
      j -= 1
    end
  end
end

# シェーカーソート
def shaker_sort(buff)
  low = 0
  high = buff.size - 1
  while low < high
    j = -1
    for i in low ... high
      if buff[i] > buff[i + 1]
        buff[i], buff[i + 1] = buff[i + 1], buff[i]
        j = i
      end
    end
    break if j == -1
    high = j
    j = -1
    i = high
    while i > low
      if buff[i - 1] > buff[i]
        buff[i - 1], buff[i] = buff[i], buff[i - 1]
        j = i
      end
      i -= 1
    end
    break if j == -1
    low = j
  end
end
    表 : バブルソート (単位 : 秒)

  個数 : 乱数  : 昇順  : 逆順  : 山型
-------+-------+-------+-------+-------
  1000 : 0.043 : 0.021 : 0.052 : 0.036
  2000 : 0.150 : 0.081 : 0.198 : 0.139
  4000 : 0.629 : 0.316 : 0.785 : 0.549
  8000 : 2.312 : 1.270 : 3.138 : 2.210

    表 : シェーカーソート (単位 : 秒)

  個数 : 乱数  : 昇順  : 逆順  : 山型
-------+-------+-------+-------+-------
  1000 : 0.039 : 0.000 : 0.063 : 0.043
  2000 : 0.152 : 0.000 : 0.241 : 0.144
  4000 : 0.578 : 0.000 : 0.963 : 0.554
  8000 : 2.320 : 0.001 : 3.837 : 2.231

●挿入ソート、選択ソート

リスト : ソート (2)

# 挿入ソート
def insert_sort(buff)
  for i in 1 ... buff.size
    temp = buff[i]
    j = i - 1
    while j >= 0 && temp < buff[j]
      buff[j + 1] = buff[j]
      j -= 1
    end
    buff[j + 1] = temp
  end
end

# 選択ソート
def select_sort(buff)
  for i in 0 ... buff.size-1
    m = i
    for j in i+1 ... buff.size
      m = j if buff[j] < buff[m]
    end
    buff[i], buff[m] = buff[m], buff[i]
  end
end
    表 : 挿入ソート (単位 : 秒)

  個数 : 乱数  : 昇順  : 逆順  : 山型
-------+-------+-------+-------+-------
  1000 : 0.017 : 0.000 : 0.030 : 0.015
  2000 : 0.062 : 0.000 : 0.143 : 0.075
  4000 : 0.269 : 0.001 : 0.477 : 0.230
  8000 : 0.903 : 0.001 : 1.826 : 0.914

    表 : 選択ソート (単位 : 秒)

       : 乱数  : 昇順  : 逆順  : 山型
-------+-------+-------+-------+-------
  1000 : 0.042 : 0.036 : 0.039 : 0.038
  2000 : 0.142 : 0.144 : 0.147 : 0.145
  4000 : 0.560 : 0.561 : 0.580 : 0.570
  8000 : 2.243 : 2.248 : 2.367 : 2.268

●シェルソート, コームソート

リスト : ソート (3)

# シェルソート
def shell_sort(buff)
  gap = 1
  gap = gap * 3 + 1 while gap < buff.size / 9
  while gap > 0
    for i in gap ... buff.size
      temp = buff[i]
      j = i - gap
      while j >= 0 && temp < buff[j]
        buff[j + gap] = buff[j]
        j -= gap
      end
      buff[j + gap] = temp
    end
    gap /= 3
  end
end

# コームソート
def comb_sort(buff)
  gap = buff.size
  done = false
  while gap > 1 || (not done)
    gap = (gap * 10) / 13
    gap = 1 if gap == 0
    gap = 11 if gap == 9 || gap == 10
    done = true
    for i in 0 ... (buff.size - gap)
      if buff[i] > buff[i + gap]
        buff[i], buff[i + gap] = buff[i + gap], buff[i]
        done = false
      end
    end
  end
end
    表 : シェルソート (単位 : 秒)

  個数 : 乱数  : 昇順  : 逆順  : 山型
-------+-------+-------+-------+-------
 10000 : 0.023 : 0.009 : 0.013 : 0.013
 20000 : 0.046 : 0.019 : 0.028 : 0.027
 40000 : 0.103 : 0.040 : 0.058 : 0.054
 80000 : 0.226 : 0.084 : 0.123 : 0.113
160000 : 0.553 : 0.183 : 0.256 : 0.243
320000 : 1.164 : 0.399 : 0.535 : 0.516

    表 : コームソート (単位 : 秒)

  個数 : 乱数  : 昇順  : 逆順  : 山型
-------+-------+-------+-------+-------
 10000 : 0.030 : 0.021 : 0.026 : 0.024
 20000 : 0.063 : 0.044 : 0.050 : 0.051
 40000 : 0.137 : 0.101 : 0.109 : 0.116
 80000 : 0.276 : 0.212 : 0.233 : 0.244
160000 : 0.617 : 0.449 : 0.491 : 0.505
320000 : 1.288 : 0.962 : 1.040 : 1.085

●クイックソート

リスト : ソート (4)

# 中央値を返す
def median3(a, b, c)
  if a > b
    b > c ? b : (a < c ? a : c)
  else
    b < c ? b : (a < c ? c : a)
  end
end

#
# 再帰版
#
def quick_sort(buff, low, high)
  p = median3(buff[low], buff[(low + high) / 2], buff[high])
  i, j = low, high
  loop do
    i += 1 while buff[i] < p
    j -= 1 while buff[j] > p
    break if i >= j
    buff[i], buff[j] = buff[j], buff[i]
    i += 1
    j -= 1
  end
  quick_sort(buff, low, i - 1) if i - low > 1
  quick_sort(buff, j + 1, high) if high - j > 1
end

#
# 非再帰版
#

# 定数
LIMIT = 10

# 挿入ソート
def insert_sort2(buff, low, high)
  for i in low+1 .. high
    temp = buff[i]
    j = i - 1
    while j >= low && temp < buff[j]
      buff[j + 1] = buff[j]
      j -= 1
    end
    buff[j + 1] = temp
  end
end

def quick_sort1(buff)
  low_stack = []
  high_stack = []
  low = 0
  high = buff.size - 1
  loop do
    loop do
      if high - low <= LIMIT
        insert_sort2(buff, low, high)
        break
      end
      p = median3(buff[low], buff[(low + high) / 2], buff[high])
      i = low
      j = high
      loop do
        i += 1 while buff[i] < p
        j -= 1 while buff[j] > p
        break if i >= j
        buff[i], buff[j] = buff[j], buff[i]
        i += 1
        j -= 1
      end
      if i - low > high - j
        low_stack.push low
        high_stack.push(i - 1)
        low = j + 1
      else
        low_stack.push(j + 1)
        high_stack.push high
        high = i - 1
      end
    end
    break if low_stack.empty?
    low = low_stack.pop
    high = high_stack.pop
  end
end
    表 : クイックソート (単位 : 秒)
            [再帰版]

  個数 : 乱数  : 昇順  : 逆順  : 山型
-------+-------+-------+-------+-------
 10000 : 0.014 : 0.005 : 0.006 : 0.017
 20000 : 0.027 : 0.012 : 0.013 : 0.037
 40000 : 0.055 : 0.022 : 0.025 : 0.081
 80000 : 0.112 : 0.046 : 0.051 : 0.171
160000 : 0.232 : 0.094 : 0.106 : 0.365
320000 : 0.487 : 0.200 : 0.212 : 0.794

    表 : クイックソート (単位 : 秒)
           [非再帰版]

  個数 : 乱数  : 昇順  : 逆順  : 山型
-------+-------+-------+-------+-------
 10000 : 0.011 : 0.005 : 0.005 : 0.017
 20000 : 0.023 : 0.010 : 0.010 : 0.036
 40000 : 0.048 : 0.020 : 0.023 : 0.075
 80000 : 0.102 : 0.042 : 0.047 : 0.167
160000 : 0.213 : 0.088 : 0.095 : 0.353
320000 : 0.447 : 0.178 : 0.198 : 0.772

●ヒープソート, マージソート

リスト : ヒープソート

def heap_sort(buff)
  i = buff.size / 2 - 1
  while i >= 0
    n = i
    loop do
      c = 2 * n + 1
      break if c >= buff.size
      if c + 1 < buff.size
        c += 1 if buff[c] < buff[c + 1]
      end
      break if buff[n] >= buff[c]
      buff[n], buff[c] = buff[c], buff[n]
      n = c
    end
    i -= 1
  end
  i = buff.size - 1
  while i > 0
    buff[0], buff[i] = buff[i], buff[0]
    n = 0
    loop do
      c = 2 * n + 1
      break if c >= i
      if c + 1 < i
        c += 1 if buff[c] < buff[c + 1]
      end
      break if buff[n] >= buff[c]
      temp = buff[n]
      buff[n], buff[c] = buff[c], buff[n]
      n = c
    end
    i -= 1
  end
end
    表 : ヒープソート (単位 : 秒)

  個数 : 乱数  : 昇順  : 逆順  : 山型
-------+-------+-------+-------+-------
 10000 : 0.033 : 0.029 : 0.031 : 0.029
 20000 : 0.061 : 0.061 : 0.056 : 0.060
 40000 : 0.127 : 0.134 : 0.120 : 0.125
 80000 : 0.270 : 0.282 : 0.256 : 0.264
160000 : 0.571 : 0.575 : 0.534 : 0.571
320000 : 1.228 : 1.285 : 1.128 : 1.183
リスト : マージソート

# 改良版
def merge_sort(a, b, low, high)
  if high - low <= LIMIT
    insert_sort2(b, low, high)
  else
    mid = (low + high) / 2
    merge_sort(b, a, low, mid)
    merge_sort(b, a, mid + 1, high)
    i = low
    j = mid + 1
    k = low
    while i <= mid and j <= high
      if a[i] <= a[j]
        b[k] = a[i]
        i += 1
      else
        b[k] = a[j]
        j += 1
      end
      k += 1
    end
    while i <= mid
      b[k] = a[i]
      k += 1
      i += 1
    end
    while j <= high
      b[k] = a[j]
      k += 1
      j += 1
    end
  end
end

def msort(buff)
  work = buff.dup
  merge_sort(work, buff, 0, buff.size - 1)
end
    表 : マージソート (単位 : 秒)

       : 乱数  : 昇順  : 逆順  : 山型
-------+-------+-------+-------+-------
 10000 : 0.012 : 0.008 : 0.012 : 0.009
 20000 : 0.026 : 0.017 : 0.024 : 0.020
 40000 : 0.054 : 0.035 : 0.047 : 0.041
 80000 : 0.112 : 0.076 : 0.095 : 0.088
160000 : 0.238 : 0.162 : 0.200 : 0.184
320000 : 0.524 : 0.343 : 0.419 : 0.386

●プログラムリスト

# coding: utf-8
#
# sort.rb : ソート
#
#           Copyright (C) 2023 Makoto Hiroi
#

# テストデータの生成
def make_data(x)
  # x は生成するデータの個数
  # 乱数
  a = (1..x).to_a.shuffle
  # 昇順
  b = (1..x).to_a
  # 降順
  c = b.reverse
  # 山型
  d = (1..x/2).to_a + (1..x/2).to_a.reverse
  return a, b, c, d
end

# ソートのチェック
def check(buff)
  for i in 0 ... buff.size-1
    raise "sort error" if buff[i] > buff[i + 1]
  end
end

# 時間計測
def sort_test(nums, &fn)
  print "  個数 : 乱数  : 昇順  : 逆順  : 山型  \n"
  print "-------+-------+-------+-------+-------\n"
  for n in nums
    printf "%6d ", n
    for buff in make_data(n)
      s = Time.now
      fn.call(buff)
      e = Time.now
      check(buff)
      printf ": %.3f ", e - s
    end
    print "\n"
  end
end

#
# バブルソート
#
def buble_sort(buff)
  for i in 0 ... buff.size
    j = buff.size - 1
    while j > i
      if buff[j - 1] > buff[j]
        buff[j], buff[j - 1] = buff[j - 1], buff[j]
      end
      j -= 1
    end
  end
end

#
# 挿入ソート
#
def insert_sort(buff)
  for i in 1 ... buff.size
    temp = buff[i]
    j = i - 1
    while j >= 0 && temp < buff[j]
      buff[j + 1] = buff[j]
      j -= 1
    end
    buff[j + 1] = temp
  end
end

#
# 選択ソート
#
def select_sort(buff)
  for i in 0 ... buff.size-1
    m = i
    for j in i+1 ... buff.size
      m = j if buff[j] < buff[m]
    end
    buff[i], buff[m] = buff[m], buff[i]
  end
end

#
# シェーカーソート
#
def shaker_sort(buff)
  low = 0
  high = buff.size - 1
  while low < high
    j = -1
    for i in low ... high
      if buff[i] > buff[i + 1]
        buff[i], buff[i + 1] = buff[i + 1], buff[i]
        j = i
      end
    end
    break if j == -1
    high = j
    j = -1
    i = high
    while i > low
      if buff[i - 1] > buff[i]
        buff[i - 1], buff[i] = buff[i], buff[i - 1]
        j = i
      end
      i -= 1
    end
    break if j == -1
    low = j
  end
end

#
# シェルソート
#
def shell_sort(buff)
  gap = 1
  gap = gap * 3 + 1 while gap < buff.size / 9
  while gap > 0
    for i in gap ... buff.size
      temp = buff[i]
      j = i - gap
      while j >= 0 && temp < buff[j]
        buff[j + gap] = buff[j]
        j -= gap
      end
      buff[j + gap] = temp
    end
    gap /= 3
  end
end

#
# コームソート
#
def comb_sort(buff)
  gap = buff.size
  done = false
  while gap > 1 || (not done)
    gap = (gap * 10) / 13
    gap = 1 if gap == 0
    gap = 11 if gap == 9 || gap == 10
    done = true
    for i in 0 ... (buff.size - gap)
      if buff[i] > buff[i + gap]
        buff[i], buff[i + gap] = buff[i + gap], buff[i]
        done = false
      end
    end
  end
end

#
# クイックソート
#

# 中央値を返す
def median3(a, b, c)
  if a > b
    b > c ? b : (a < c ? a : c)
  else
    b < c ? b : (a < c ? c : a)
  end
end

# 再帰版
def quick_sort(buff, low, high)
  p = median3(buff[low], buff[(low + high) / 2], buff[high])
  i, j = low, high
  loop do
    i += 1 while buff[i] < p
    j -= 1 while buff[j] > p
    break if i >= j
    buff[i], buff[j] = buff[j], buff[i]
    i += 1
    j -= 1
  end
  quick_sort(buff, low, i - 1) if i - low > 1
  quick_sort(buff, j + 1, high) if high - j > 1
end

# 定数
LIMIT = 10

# 挿入ソート
def insert_sort2(buff, low, high)
  for i in low+1 .. high
    temp = buff[i]
    j = i - 1
    while j >= low && temp < buff[j]
      buff[j + 1] = buff[j]
      j -= 1
    end
    buff[j + 1] = temp
  end
end

# 非再帰版
def quick_sort1(buff)
  low_stack = []
  high_stack = []
  low = 0
  high = buff.size - 1
  loop do
    loop do
      if high - low <= LIMIT
        insert_sort2(buff, low, high)
        break
      end
      p = median3(buff[low], buff[(low + high) / 2], buff[high])
      i = low
      j = high
      loop do
        i += 1 while buff[i] < p
        j -= 1 while buff[j] > p
        break if i >= j
        buff[i], buff[j] = buff[j], buff[i]
        i += 1
        j -= 1
      end
      if i - low > high - j
        low_stack.push low
        high_stack.push(i - 1)
        low = j + 1
      else
        low_stack.push(j + 1)
        high_stack.push high
        high = i - 1
      end
    end
    break if low_stack.empty?
    low = low_stack.pop
    high = high_stack.pop
  end
end

#
# マージソート
#

# 改良版
def merge_sort(a, b, low, high)
  if high - low <= LIMIT
    insert_sort2(b, low, high)
  else
    mid = (low + high) / 2
    merge_sort(b, a, low, mid)
    merge_sort(b, a, mid + 1, high)
    i = low
    j = mid + 1
    k = low
    while i <= mid and j <= high
      if a[i] <= a[j]
        b[k] = a[i]
        i += 1
      else
        b[k] = a[j]
        j += 1
      end
      k += 1
    end
    while i <= mid
      b[k] = a[i]
      k += 1
      i += 1
    end
    while j <= high
      b[k] = a[j]
      k += 1
      j += 1
    end
  end
end

def msort(buff)
  work = buff.dup
  merge_sort(work, buff, 0, buff.size - 1)
end

#
# ヒープソート
#
def heap_sort(buff)
  i = buff.size / 2 - 1
  while i >= 0
    n = i
    loop do
      c = 2 * n + 1
      break if c >= buff.size
      if c + 1 < buff.size
        c += 1 if buff[c] < buff[c + 1]
      end
      break if buff[n] >= buff[c]
      buff[n], buff[c] = buff[c], buff[n]
      n = c
    end
    i -= 1
  end
  i = buff.size - 1
  while i > 0
    buff[0], buff[i] = buff[i], buff[0]
    n = 0
    loop do
      c = 2 * n + 1
      break if c >= i
      if c + 1 < i
        c += 1 if buff[c] < buff[c + 1]
      end
      break if buff[n] >= buff[c]
      temp = buff[n]
      buff[n], buff[c] = buff[c], buff[n]
      n = c
    end
    i -= 1
  end
end

Copyright (C) 2023 Makoto Hiroi
All rights reserved.

[ PrevPage | R u b y | NextPage ]