【アルゴリズムの勉強】Rubyで二分探索(バイナリサーチ)を書いてみる

アルゴリズムの勉強としてRubyで二分探索(バイナリサーチ)を実装する。

二分探索とは

ソート済みのリストや配列に入ったデータ(同一の値はないものとする)に対する検索を行うにあたって、 中央の値を見て、検索したい値との大小関係を用いて、検索したい値が中央の値の右にあるか、左にあるかを判断して、片側には存在しないことを確かめながら検索していく。
大小関係を用いるため、未ソートのリストや大小関係の定義されない要素を含むリストには二分探索を用いることはできない。
n個のデータがある場合、時間計算量はOlog2nである(O記法)。
n個のデータの中央の値を見ることで、1回の操作でn/2個程度(奇数の場合は(n-1)/2個、偶数の場合はn/2個または(n/2)-1個)の要素を無視することができる。

二分探索 - Wikipedia

Rubyで実装

arr = [2, 4, 6, 9, 10, 12, 15, 20, 25, 60]
number_of_elements = arr.count

def binary_search(arr, number_of_elements, target)
  left = 0
  right = number_of_elements - 1

  while left <= right
    center = (left + right) / 2
    if arr[center] == target
      return center
    elsif arr[center] < target
      left = center + 1
    else
      right = center - 1
    end
  end

  return -1
end

target = 20
result = binary_search(arr, number_of_elements, target)
if result == -1
  puts "値が#{target}の要素は配列内に存在しません"
else
  puts "要素の値が#{target}のインデックス => #{result}"
end

参考文献、参考リンク

【アルゴリズムの勉強】Rubyで番兵法を用いた線形探索を書いてみる

アルゴリズムの勉強としてRubyで番兵法を用いた線形探索を実装する。

番兵法とは

データの終了を示すために配置される特殊なデータを指す。この用語は、微妙に異なる以下の2つの意味で使われる。
・実データには出現しない、データの終了を表すための専用の値
・入力データを処理するループの終了条件が複数ある場合に、条件判定の数を削減するために置くダミーのデータ

番兵 - Wikipedia

Rubyで実装

arr = [4, 20, 6, 10, 2, 15, 60, 9, 25, 12]
number_of_elements = arr.count

def linear_search_with_sentinel_method(arr, number_of_elements, target)
  index = 0
  arr[number_of_elements] = target

  loop do
    break if arr[index] == target
    index += 1
  end

  if index == number_of_elements
    return -1
  else
    return index
  end
end

target = 10
result = linear_search_with_sentinel_method(arr, number_of_elements, target)
if result == -1
  puts "値が#{target}の要素は配列内に存在しません"
else
  puts "要素の値が#{target}のインデックス => #{result}"
end

参考文献、参考リンク

【アルゴリズムの勉強】Rubyで線形探索を書いてみる

アルゴリズムの勉強としてRubyで線形探索を実装する。

線形探索とは

線形探索(せんけいたんさく、英: linear search, sequential search)は、検索のアルゴリズムの一つ。 リストや配列に入ったデータに対する検索を行うにあたって、 先頭から順に比較を行い、それが見つかれば終了する。
{\displaystyle n} n個のデータから {\displaystyle m} m個のデータを検索する場合、 時間計算量は {\displaystyle O(nm)} O(nm)、空間計算量は {\displaystyle O(1)} O(1)必要となる。

線型探索 - Wikipedia

Rubyで実装

arr = [4, 20, 6, 10, 2, 15, 60, 9, 25, 12]
number_of_elements = arr.count

def linear_search(arr, number_of_elements, target)
  index = 0
  loop do
    if index == number_of_elements
      return -1
    elsif arr[index] == target
      return index
    end
    index += 1
  end
end

target = 9
result = linear_search(arr, number_of_elements, target)
if result == -1
  puts "値が#{target}の要素は配列内に存在しません"
else
  puts "要素の値が#{target}のインデックス => #{result}"
end

参考文献、参考リンク

【アルゴリズムの勉強】Rubyで分布数え上げソート(カウンティングソート、度数ソート)を書いてみる

アルゴリズムの勉強としてRubyで分布数え上げソート(カウンティングソート、度数ソートとも呼ばれる)を実装する。

分布数え上げソートとは

分布数え上げソートはソート対象のデータをキーにして、キーの出現回数とその累積度数分布を計算して利用することで整列を行うアルゴリズムです。 バケットソートと同じくキーとなるデータがとりうる値の範囲をあらかじめ知っている必要があります。バケットソートとの違いはキーの重複に対応したアルゴリズムであるということです。

分布数え上げソート : アルゴリズム

Rubyで実装

arr = [4, 20, 6, 10, 2, 15, 60, 9, 25, 12, 9]
number_of_elements = arr.count
max = arr.max

def counting_sort(arr, number_of_elements, max)
  counting_arr = Array.new(max + 1, 0)
  answer = []

  for i in 0..(number_of_elements - 1)
    counting_arr[arr[i]] += 1
  end

  for i in 1..max
    counting_arr[i] += counting_arr[i - 1]
  end

  (number_of_elements - 1).downto(0) {|i|
    key = counting_arr[arr[i]] - 1
    answer[key] = arr[i]
    counting_arr[arr[i]] += -1
  }
  return answer
end

sorted_arr = counting_sort(arr, number_of_elements, max)
p sorted_arr
# => [2, 4, 6, 9, 9, 10, 12, 15, 20, 25, 60]

参考文献、参考リンク

【アルゴリズムの勉強】Rubyでヒープソートを書いてみる

アルゴリズムの勉強としてRubyヒープソートを実装する。

ヒープソートとは

ヒープソート (heap sort) とはリストの並べ替えを二分ヒープ木を用いて行うソートのアルゴリズムである[2](ヒープ領域とは無関係であることに注意する)。
アルゴリズムは、以下のように2つの段階から構成される。
未整列のリストから要素を取り出し、順にヒープに追加する。すべての要素を追加するまで繰り返し。
ルート(最大値または最小値)を取り出し、整列済みリストに追加する。すべての要素を取り出すまで繰り返し。
計算量は O {\displaystyle (n\log n)} (n\log n) となる[2]。安定ソートではない[2]。

ヒープソート - Wikipedia

Rubyで実装

arr = [4, 20, 6, 10, 2, 15, 60, 9, 25, 12]
number_of_elements = arr.count

def make_heap(arr, parent, end_element)
  temp = arr[parent]
  child = 0

  while parent < (end_element + 1) / 2
    left_child = (parent * 2) + 1
    right_child = left_child + 1

    if right_child <= end_element && arr[right_child] > arr[left_child]
      child = right_child
    else
      child = left_child
    end

    if temp >= arr[child]
      break
    end

    arr[parent] = arr[child]
    parent = child
  end

  arr[parent] = temp
end

def heap_sort(arr, number_of_elements)
  center = (number_of_elements - 1) / 2
  while center >= 0
    make_heap(arr, center, number_of_elements - 1)
    center += -1
  end

  end_element = number_of_elements - 1
  while end_element > 0
    arr[0], arr[end_element] = arr[end_element], arr[0]
    make_heap(arr, 0, end_element - 1)
    end_element += -1
  end
end

heap_sort(arr, number_of_elements)
p arr
# => [2, 4, 6, 9, 10, 12, 15, 20, 25, 60]

参考文献、参考リンク

【アルゴリズムの勉強】Rubyでマージソートを書いてみる

アルゴリズムの勉強としてRubyマージソートを実装する。

マージソートとは

マージソートは、ソートのアルゴリズムで、既に整列してある複数個の列を1個の列にマージする際に、小さいものから先に新しい列に並べれば、新しい列も整列されている、というボトムアップの分割統治法による。大きい列を多数の列に分割し、そのそれぞれをマージする作業は並列化できる。
n個のデータを含む配列をソートする場合、最悪計算量O(n log n)である。分割と統合の実装にもよるが、一般に安定なソートを実装できる。インプレースなソートも提案されているが、通常O(n)の外部記憶を必要とする[1]。
(ナイーブな)クイックソートと比べると、最悪計算量は少ない[1]。ランダムなデータでは通常、クイックソートのほうが速い。

マージソート - Wikipedia

Rubyで実装

arr = [4, 20, 6, 10, 2, 15, 60, 9, 25, 12]
number_of_elements = arr.count
buffer = []

def merge_sort(arr, left, right, buffer)
  if left < right
    center = (left + right) / 2
    first_r = 0
    first_l = 0
    right_cursor = left
    left_cursor = left

    merge_sort(arr, left, center, buffer)
    merge_sort(arr, center + 1, right, buffer)

    while right_cursor <= center
      buffer[first_r] = arr[right_cursor]
      first_r += 1
      right_cursor += 1
    end

    while right_cursor <= right && first_l < first_r
      if buffer[first_l] <= arr[right_cursor]
        arr[left_cursor] = buffer[first_l]
        left_cursor += 1
        first_l += 1
      else
        arr[left_cursor] = arr[right_cursor]
        left_cursor += 1
        right_cursor += 1
      end
    end

    while first_l < first_r
      arr[left_cursor] = buffer[first_l]
      left_cursor += 1
      first_l += 1
    end
  end
  return arr
end

sorted_arr = merge_sort(arr, 0, number_of_elements - 1, buffer)
p sorted_arr
# => [2, 4, 6, 9, 10, 12, 15, 20, 25, 60]

参考文献、参考リンク

【アルゴリズムの勉強】Rubyでクイックソートを書いてみる

アルゴリズムの勉強としてRubyクイックソートを実装する。

クイックソートとは

クイックソート (quicksort) は、1960年にアントニー・ホーアが開発したソートのアルゴリズム。分割統治法の一種。
n個のデータをソートする際の最良計算量および平均計算量はO {\displaystyle (n\log n)} (n\log n)である。他のソート法と比べて、一般的に最も高速だといわれているが対象のデータの並びやデータの数によっては必ずしも速いわけではなく、最悪の計算量はO {\displaystyle (n^{2})} (n^{2})である。また数々の変種がある。 安定ソートではない。

クイックソート - Wikipedia

Rubyで実装

arr = [4, 20, 6, 10, 15, 2, 60, 9, 25, 12]
number_of_elements = arr.count

def quick_sort(arr, left_end, right_end)
  left_point = left_end
  right_point = right_end
  pivot = arr[(left_point + right_point) / 2]

  while left_point <= right_point
    while arr[left_point] < pivot
      left_point += 1
    end
    while arr[right_point] > pivot
      right_point += -1
    end
    if (left_point <= right_point)
      arr[left_point], arr[right_point] = arr[right_point], arr[left_point]
      left_point += 1
      right_point += -1
    end
  end

  quick_sort(arr, left_end, right_point) if left_end < right_point
  quick_sort(arr, left_point, right_end) if left_point < right_end
end

quick_sort(arr, 0, number_of_elements - 1)
p arr
# => [2, 4, 6, 9, 10, 12, 15, 20, 25, 60]

参考文献