木曜日, 6月 19, 2025
No menu items!
ホーム ブログ ページ 5015

二分探索とDBインデックスについて [AtCoderに入門] #Ruby – Qiita



二分探索とDBインデックスについて [AtCoderに入門] #Ruby - Qiita

はじめに

最近、自己学習や業務でパフォーマンスを意識したコーディングができていないと感じることが増えてきました。単純な処理なのに実行時間が長すぎるみたいなことがあり、このままじゃマズいなと焦りを感じていました。特に大量データを扱うようになってから、コードの書き方一つでパフォーマンスが大きく変わることを痛感しています。

そこで、パフォーマンスの良いコードを書くためにはアルゴリズムの知識を身に付けたいと興味が湧いてきました。アルゴリズムには苦手意識がありましたがやる気があるうちに学んでいきたいと思います。
目先の目標はAtCoderで水色(Bランク R1200~1599 上位15%)になることです。楽しくコーディングできるようになるまで辛抱して毎日学習していこうと思います。

学習のために購入したのが以下の本です。

アルゴリズム、競技プログラミング

システム・SQL

アルゴリズムの学習と並行して、アプリケーションパフォーマンスにも興味が広がってきました。特にデータベースのクエリ最適化について調べる中で、インデックスの仕組みについても軽く学習して自由自在に使いこなせるようになりたいと思いました。

今回は、自分の学習メモとして、アルゴリズムの基本である二分探索について整理してみました。二分探索とデータベースのインデックスには共通する考え方があり、この関連性を理解することで両方の知識を深められると感じています。この記事は自分の理解を深めるためのものですが、同じように学んでいる方の参考になれば嬉しいです。

二分探索とは

ニ分探索(Binary Search)は、ソート済みの配列から特定の値を見つけるための効率的なアルゴリズムです。線形探索がO(n)の時間複雑度であるのに対し、二分探索はO(log n)という非常に高速な時間複雑度を持ちます。

二分探索の基本的な手順

  1. ソート済みの配列の中央の要素を確認する
  2. 探している値と中央の要素を比較する
  3. 探している値が中央の要素より小さければ、配列の前半部分で再度探索を行う
  4. 探している値が中央の要素より大きければ、配列の後半部分で再度探索を行う
  5. 探している値が見つかるか、探索範囲が空になるまで繰り返す

イメージで理解するための参考youtube
https://www.youtube.com/shorts/SFtNiIQZHRQ
https://www.youtube.com/watch?v=i9LAfRbGkT8

Rubyでの基本的な二分探索の実装

def binary_search(array, target)
  # 探索範囲の初期化
  low = 0                 # 探索範囲の下限
  high = array.length - 1 # 探索範囲の上限

  # 探索範囲が存在する限り繰り返す
  while low  high
    # 中央の要素のインデックスを計算
    # Ruby では整数同士の除算は自動的に切り捨てられる
    mid = (low + high) / 2

    # 中央の要素と目標値を比較
    if array[mid] == target
      return mid          # 目標値が見つかった場合、そのインデックスを返す
    elsif array[mid]  target
      low = mid + 1   # 目標値が中央値より大きい場合、後半部分を探索
    else
      high = mid - 1  # 目標値が中央値より小さい場合、前半部分を探索
    end
  end

  # 目標値が見つからなかった場合は -1 を返す
  return -1
end

# 使用例
arr = [1, 3, 5, 7, 9, 11, 13, 15]
puts binary_search(arr, 9) # => 4 (9は配列の四番目の要素、インデックスは4)
puts binary_search(arr, 6) # => -1 (6は配列に存在しないため-1を返す)

# 二分探索の時間複雑度: O(log n)
# 各ステップで探索範囲が半分になるため、非常に効率的なアルゴリズム
# ただし、配列があらかじめソートされている必要がある

データベースのインデックスと二分探索 (AIに教えてもらったため、合っているかは不明)

データベースのインデックスは、二分探索のアイデアを活用した仕組みです。インデックスを貼ることで、データベースの検索操作が大幅に高速化されます。

インデックスがなぜ検索を高速化するのか (AIに教えてもらったため、合っているかは不明)

データベースにインデックスがない場合、特定のレコードを探すためには全テーブルをスキャン(フルテーブルスキャン)する必要があり、これはO(n)の時間がかかります。一方、インデックスを使用すると、二分探索のようにO(log n)の時間で目的のレコードに到達できます。
具体的には、インデックスは指定したカラムの値をソートした形で別途保存し、各値に対して元のテーブルのどの位置にデータがあるかを示すポインタを持ちます。これにより、条件に合致するデータの位置を二分探索的に素早く特定できるのです。

インデックスを貼るべきカラムの特徴 (AIに教えてもらったため、合っているかは不明)

  1. WHERE句でよく使われるカラム:検索条件に頻繁に使われるカラムにインデックスを貼ると、検索が高速になります。
  2. JOIN操作で使用されるカラム:テーブル結合に使われるカラムにインデックスを貼ると、JOIN操作が高速化されます。
  3. 一意性の高いカラム:重複値が少ないカラム(カーディナリティが高いカラム)ほどインデックスの効果が高くなります。例えば、性別(男/女)のような値は種類が少なすぎるためあまり効果がありません。
  4. ORDER BY, GROUP BYでよく使われるカラム:並べ替えや集約にも二分探索的な操作が関わるため、これらの句で使われるカラムにインデックスを貼ると効果的です。

インデックスを貼るべきでないケース (AIに教えてもらったため、合っているかは不明)

  1. 更新が頻繁に行われるテーブル:インデックスがあると挿入・更新・削除の度にインデックスも更新する必要があり、処理が遅くなります。
  2. 小さなテーブル:レコード数が少ない場合、フルテーブルスキャンの方が効率的なことがあります。
  3. カーディナリティの低いカラム:値の種類が少ないカラムはインデックスの恩恵が少ないです。

データベースのインデックス実装と二分探索の関係 (AIに教えてもらったため、合っているかは不明)

参考記事
B-treeインデックス入門

データベースのインデックスは内部的に「B木」や「B+木」という特殊なデータ構造を使用していることが多いです。これらは二分探索木を拡張したもので、多くの子ノードを持つことができ、ディスクアクセスの回数を減らすよう最適化されています。
しかし基本的な考え方は二分探索と同じで、以下のように動作します:

  1. インデックスの根ノードから探索を開始する
  2. 探している値が現在のノードのどの範囲に入るかを判断する
  3. その範囲に対応する子ノードに移動する
  4. 目的の値が見つかるまで繰り返す

このようにインデックスは二分探索の考え方を応用し、大量のデータからでも効率的に目的のレコードを見つけられるようにしています。

インデックスの実例

例えば、ユーザーテーブルのemailカラムによく使うクエリがあるとします:

SELECT * FROM users WHERE email = '[email protected]';

このクエリに対してemailカラムにインデックスがない場合、データベースは全てのレコードを一つずつ調べる必要があります(O(n))。しかしemailカラムにインデックスを貼ると、二分探索的な手法で目的のレコードを素早く見つけることができます(O(log n))。
インデックスの作成は以下のようなSQLで行います:

CREATE INDEX index_users_on_email ON users(email);

Rubyで解く二分探索問題

問題1: 挿入位置の検索

ソート済みの配列に新しい値を挿入する位置を二分探索で見つけてください。

def find_insert_position(arr, target)
  low = 0
  high = arr.length - 1

  while low  high
    # 中央の要素のインデックスを計算
    mid = (low + high) / 2

    # 中央の要素と挿入したい値を比較
    if arr[mid] == target
      return mid          # 同じ値が見つかった場合、そのインデックスを返す
    elsif arr[mid]  target
      low = mid + 1       # 挿入したい値が中央値より大きい場合、右側(後半)を探索
    else
      high = mid - 1      # 挿入したい値が中央値より小さい場合、左側(前半)を探索
    end
  end

  # ループを抜けた時点でのlowが挿入位置となる
  # これは次の理由による:
  # - 探索が終了した時点で、low > high の状態
  # - low は「targetより小さい最後の要素の次の位置」を指している
  return low
end


# テスト例
arr = [1, 3, 5, 7, 9]
puts find_insert_position(arr, 6)  # => 3(6は5(インデックス2)と7(インデックス3)の間に入るため、インデックス3が挿入位置)
puts find_insert_position(arr, 10) # => 5(10は全ての要素より大きいため、配列の最後(length=5の位置)が挿入位置)
puts find_insert_position(arr, 0)  # => 0(0は全ての要素より小さいため、配列の先頭が挿入位置)
puts find_insert_position(arr, 5)  # => 2(5と同じ値がある場合、その位置を返す)

二分探索で値が見つからない場合、以下の流れで進みます:

最初に low = 0, high = arr.length - 1 で初期化します
while low のループの中で探索を続けます
値が見つからない場合は、以下のどちらかの処理を毎回行います:

arr[mid] の場合:low = mid + 1 (右側を探索)
arr[mid] > target の場合:high = mid - 1 (左側を探索)

最終的に探索範囲が狭まり続け、値が見つからない場合は以下のようになります:

  • 探索範囲が1要素だけになった時点で、その要素と比較します
  • その要素がtargetより小さければ low = mid + 1 となり、low > high になります
  • その要素がtargetより大きければ high = mid - 1 となり、同様に low > high になります

具体例で見てみましょう。配列 [1, 3, 5, 7, 9] で値 6 を探す場合:

low = 0, high = 4, mid = 2arr[2] = 5 なので low = 3
low = 3, high = 4, mid = 3arr[3] = 7 > 6 なので high = 2
ここで low = 3, high = 2 となり、low > high なのでループを抜けます

この時の low の値は、ちょうど「targetより小さい最後の要素の次の位置」を指しており、これが適切な挿入位置になります。これは「6」の場合、「5」と「7」の間に入るべき位置なので、インデックス3が正しい挿入位置になります。

問題2: 回転配列の最小値

元々ソートされていた配列が何回か回転された後の配列の最小値を二分探索で見つけてください。

def find_min_in_rotated(arr)
  # 探索範囲の初期化
  left = 0                 # 探索範囲の左端
  right = arr.length - 1   # 探索範囲の右端

  # 左端と右端が一致するまで(つまり、要素が1つになるまで)探索を続ける
  while left  right
    # 中央の要素のインデックスを計算
    mid = (left + right) / 2

    # 配列が回転している場合、最小値は「不連続点」に存在する
    # 中央値と右端の値を比較することで、どちら側に不連続点があるかを判断
    if arr[mid] > arr[right]
      # 中央値が右端の値より大きい場合、不連続点(つまり最小値)は右半分にある
      # 例: [4,5,6,7,0,1,2]の場合、arr[3]=7 > arr[6]=2なので、最小値は右半分にある
      left = mid + 1
    else
      # 中央値が右端の値以下の場合、不連続点は左半分か中央値自身
      # 例: [5,1,2,3,4]の場合、arr[2]=2 
      # midが最小値の可能性があるので、midを除外せずrightをmidにする
      right = mid
    end
  end

  # ループを抜けた時点でleftとrightは同じ位置を指し、それが最小値の位置
  return arr[left]
end

# テスト例
puts find_min_in_rotated([4, 5, 6, 7, 0, 1, 2])  # => 0 ([0,1,2,4,5,6,7]が3回右に回転)
puts find_min_in_rotated([3, 4, 5, 1, 2])        # => 1 ([1,2,3,4,5]が2回右に回転)
puts find_min_in_rotated([11, 13, 15, 17])       # => 11 (回転していない場合も正しく動作)
puts find_min_in_rotated([5, 1, 2, 3, 4])        # => 1 ([1,2,3,4,5]が1回右に回転)

# 解説:
# 回転配列の特性:元々ソートされていた配列が回転されると、1箇所だけ「不連続点」ができる。
# この不連続点が最小値の位置である。
#
# このアルゴリズムのポイント:
# 1. 配列の中央値(mid)と右端の値(right)を比較
# 2. もし中央値 > 右端の値なら、不連続点は右半分にある
# 3. もし中央値 
#
# 例: [4,5,6,7,0,1,2] の場合の探索過程
# 初期状態: left=0, right=6, mid=3, arr[mid]=7, arr[right]=2
# 7 > 2 なので、left = mid + 1 = 4
# 次の状態: left=4, right=6, mid=5, arr[mid]=1, arr[right]=2
# 1 
# 次の状態: left=4, right=5, mid=4, arr[mid]=0, arr[right]=1
# 0 
# 最後の状態: left=4, right=4 (ループ終了)
# 結果: arr[left] = arr[4] = 0

問題3: ピークインデックスの検索

山型の配列(最初は増加し、ある点から減少する)のピーク要素のインデックスを二分探索で見つけてください。

def find_peak(arr)
  # 探索範囲の初期化
  left = 0                 # 探索範囲の左端
  right = arr.length - 1   # 探索範囲の右端

  # 左端と右端が一致するまで(つまり、要素が1つになるまで)探索を続ける
  while left  right
    # 中央の要素のインデックスを計算
    mid = (left + right) / 2

    # 山型配列のピークを見つけるため、中央値とその次の値を比較
    if arr[mid]  arr[mid + 1]
      # 中央値が次の値より小さい場合、まだ上昇中なのでピークは右側に存在
      # 例: [1,3,5,7,6,4,2]のmid=2では、arr[2]=5 
      left = mid + 1
    else
      # 中央値が次の値以上の場合、すでに下降しているかピーク自体なので、
      # ピークはmid自身かその左側に存在
      # 例: [1,3,5,7,6,4,2]のmid=4では、arr[4]=6 > arr[5]=4なので、ピークはmidか左側
      right = mid
    end
  end

  # ループを抜けた時点でleftとrightは同じ位置を指し、それがピーク要素の位置
  return left
end

# テスト例
puts find_peak([1, 3, 5, 7, 6, 4, 2])  # => 3(値7のインデックス)
puts find_peak([1, 2, 3, 1])           # => 2(値3のインデックス)
puts find_peak([1, 2, 3, 4, 5, 7, 1])  # => 5(値7のインデックス)

# 解説:
# 山型配列の特性:値が最初は増加し、ピークに達した後は減少する
# このアルゴリズムでは、配列内の「どの方向に進めばピークに近づくか」を判断して
# 二分探索の範囲を狭めていきます。
#
# このアルゴリズムのポイント:
# 1. 中央値(mid)とその次の値(mid+1)を比較
# 2. もし arr[mid] 
# 3. もし arr[mid] >= arr[mid+1] なら、すでに下降しているかピーク自体なので、ピークはmidか左側
#
# 例: [1,3,5,7,6,4,2] の場合の探索過程
# 初期状態: left=0, right=6, mid=3, arr[mid]=7, arr[mid+1]=6
# 7 > 6 なので、right = mid = 3
# 次の状態: left=0, right=3, mid=1, arr[mid]=3, arr[mid+1]=5
# 3 
# 次の状態: left=2, right=3, mid=2, arr[mid]=5, arr[mid+1]=7
# 5 
# 最後の状態: left=3, right=3 (ループ終了)
# 結果: arr[left] = arr[3] = 7(これがピーク値)

終わりに

アルゴリズムを学ぶ中で、二分探索とデータベースのインデックスに共通する考え方があることを知り、とても興味深く感じました。単純な仕組みでありながら、大きなパフォーマンス向上をもたらす二分探索の原理は、エンジニアとして知っておくべき基本だと実感しています。
今後もAtCoderで水色を目指して学習を続け、業務でもパフォーマンスを意識したコードが書けるようになりたいです。次は動的計画法や探索アルゴリズムについても学んでいく予定です。
この記事が同じようにアルゴリズムを学び始めた方の参考になれば嬉しいです。


株式会社シンシアでは、実務未経験のエンジニアの方や学生エンジニアインターンを採用し一緒に働いています。
※ シンシアにおける働き方の様子はこちら

弊社には年間100人程度の実務未経験の方に応募いただき、技術面接を実施しております。
この記事が少しでも学びになったという方は、ぜひ wantedly のストーリーもご覧いただけるととても嬉しいです!





Source link

Views: 0

精神科医・名越康文氏と『サイレントヒル2』で精神病院の奥をのぞいたらジェイムスに起きた“出来事”の輪郭が見えてきた。いよいよ物語が佳境に入りネタバレ注意の「ゲームさんぽ/よそ見」最新エピソード公開




いよいよネタバレ注意の「ゲームさんぽ/よそ見」最新回。新人の医者・学者がぶつかる「社会適応性と本人の幸せ」への問いに関する話題も展開される



Source link

Views: 0

『ゼンレスゾーンゼロ』Xbox版は2025年6月登場!大型アップデートVer.2.0も同じく6月配信予定




『ゼンレスゾーンゼロ』Xbox スポットライトで発表されました。



Source link

Views: 0

GMは、Brightdrop電気バンの需要が弱いために500人の雇用を削減します



GMは、Brightdrop電気バンの需要が弱いために500人の雇用を削減します

ゼネラルモーターズは、全電気のブライトドロップバンに対する需要が弱いため、カナダの工場で500人の労働者をレイオフしています。 CNBCによると

GMはCNBCに、この削減はドナルド・トランプ大統領によって行われている進行中の貿易戦争とは関係がないと語った。同社は、オンタリオ州のCAMI工場での2つのシフトの1つを削減しており、5月から20週間施設をアイドル状態にします。

カットは、ブライトドロップの短いがやや激動の歴史の最新のひねりです。それは別のエンティティとしてBrightDropを作成しました 2021年のGM内しかし 2023年に吸収しました。 2024年、ブライトドロップのバンは後にリコールを受けました バッテリー火災はほとんどありません。昨年後半、GMはBrightDropを動かしました シボレーブランドの下



Source link

Views: 0

強盗団を率いてお宝を奪い出す戦略アクション続編「Monaco 2」,本日発売。ローンチトレイラーも公開に



 HumbleGamesは本日(2025年4月11日),戦略アクションゲーム続編「Monaco2」を発売した。本作では,異なるスキルを持つ最大8人の盗賊団メンバーでチームを作り,厳重に警護されている大豪邸やヨットクラブ,パーティ会場に潜入しながら,現金や宝石などのお宝を奪っていく。



Source link

Views: 0

PCエンジンGTの2.6型液晶モニターが800円、保証無しの中古品 – AKIBA PC Hotline!


 NECの携帯型ゲーム機「PCエンジンGT」に搭載されている2.6型液晶モニターが、家電のケンちゃんに入荷。

 標準搭載のモニターを3.5インチサイズに交換する「PCエンジンGT改造サービス」で余った中古品モニターで、動作保証は無し。

 価格は800円で、在庫は複数あり。





Source link

Views: 0

2画面出力が可能なUSB Type C→HDMI変換アダプター – デジカメ Watch


サンワサプライ株式会社は、USB Type-Cからの映像出力をHDMI出力に変換するアダプター「500-USB092」を4月9日(水)に発売した。価格は7,255円。

USB Type-Cポートを利用して、HDMI出力を可能とするディスプレイアダプター。2系統出力に対応し、4K/30Hz×1とフルHD/60Hz×1の2画面出力が可能。なお、1画面出力時は最大で4K/30Hz出力となる。

専用ドライバーを導入することで、マルチディスプレイ環境が構築可能。Windows PCのほか、Mシリーズチップを搭載したMacで利用できる。

さらに本製品を3台利用すると、4K×3+フルHD×3の最大6画面への出力が行える(macOSの場合は最大4画面まで)。

電源はバスパワー。USBハブを併用する場合は、セルフパワーのハブが推奨されている。

  • 外形寸法:5.3×11×10.1cm
  • 重量:約54g
  • ケーブル長:10cm





Source link

Views: 0

【悲報】女さん、とんでもない“真実”に気づいてしまう



ダウンタウン
1

社会人になって今まで関わってこなかった体育会系とか陽キャの飲み会に参加して、社会には「小さい中居正広」や「小さい松本人志」がたくさんいるんだなって思った。
女性をアクセサリーや花や性的対象としてモノ化して、男性(=人間)とは異なる存在としてまなざして、扱う。
20代にも存在するのが地獄

document.write(” + ”);

続きを読む



Source link

Views: 0

「仮面ライダーゼロワン」新作一番くじ発売!必殺技を決めるライジングホッパー、ゼロツーのジオラマフィギュアが迫力満点



「仮面ライダーゼロワン」新作一番くじ発売!必殺技を決めるライジングホッパー、ゼロツーのジオラマフィギュアが迫力満点

「仮面ライダーゼロワン」新作一番くじ発売!必殺技を決めるライジングホッパー、ゼロツーのジオラマフィギュアが迫力満点



Source link

Views: 0

Nintendo Switch 2の“転売撃退活動”が始まる。悪質出品に待ったをかける – AUTOMATON


オークション・通販サイトeBayでは、早くもNintendo Switch 2(以下、Switch 2)がユーザーにより高額で“転売”されているという。しかしSwitch 2の発売は6月5日であり、商品確保の目途がたたないうちから出品されている模様。そうした出品が物議を醸した結果、同サイトでの規約違反に基づき“転売撃退”活動をするユーザーも現れている。GamesRadar+などが報じている。

Switch 2は、2017年3月3日に発売されたNintendo Switchの後継機種だ。携帯モードと、ドックに取り付けてのTVモードの両方で遊べる点などの特性を引き継ぎつつ、CPU・GPUの性能が強化され、対応する画面解像度・フレームレートも向上。また付属コントローラーとなる「Joy-Con 2」は本体から取り外しが可能ながらマグネット式での取り付けとなり、マウスのように使える機能も備わる。

Switch 2は6月5日の発売が予定されており、国内向けには4月16日11時までマイニンテンドーストアにて応募条件も設定された予約抽選受付が実施中(関連記事)。また全国のゲーム取扱店やオンラインショップでは、4月24日以降準備が整い次第、順次予約または抽選の受付が開始されるという。

そんな中で、eBayではさっそくSwitch 2が多数“転売”されているようだ。しかし、日本ではまだ予約の抽選結果が発表されておらず、また米国では予約受付開始日が延期されている状況(関連記事)。ヨーロッパ向けにも、基準を満たす一部のニンテンドーアカウントに向けて招待メール制での予約受付が始まったばかりの段階だ。つまりeBayではまだ商品を確保していない出品もおこなわれているとみられる。本稿執筆時点では送料抜きで約10万円などの、定価を大きく上回る出品も散見される。

Image Credit: eBay

こうした状況のなかで、一部ユーザーによる“転売撃退”活動も報告されている。あるユーザーはeBayにおいて20件ものSwitch 2の出品を通報して取り下げたと報告。eBayが通報を受けて対処したことを示す通知がずらりと並んでいる。eBayでは商品の転売自体は認められているが、Switch 2については少なくとも現時点では転売は不可能なようだ。

というのもeBayでは、予約商品の出品であること明記することなどがポリシーにて義務付けられている。出品のタイトルと説明に「予約商品(Presale)」であることを記載し、かつ予約商品で購入後40日以内の出荷を保証する必要があると定められている。しかしSwitch 2の発売日は先述のとおり6月5日。仮に予約抽選などに当選した場合でも、出品者は現時点では「40日以内の出荷」は不可能だろう。シンプルに規約に違反している商品として、出品されているSwitch 2の通報活動がおこなわれているようだ。

ちなみに上述のRedditスレッドではSwitch 2の出品の通報に利用できるという“テンプレート”までユーザー間で共有されており、活動が広まっている様子。少なくとも現時点で出品されているSwitch 2に対しては、今後も規約違反に基づく通報がおこなわれていくことだろう。

なお国内のオークション・マーケットプレイス系サイトにおいては、メルカリPayPayフリマなどでは出品者の手元にない商品の出品は禁止されている。もし今後発売前にSwitch 2が出品されたとしても、そうしたサイトでは規約違反の出品となりそうだ。

早くも希望小売価格を超える“転売”がおこなわれているSwitch 2。発売後に品薄となることへの懸念から生じる需要もあるのだろう。ただ任天堂は過去に投資家向けの質疑応答にて、転売対策としてユーザーの需要を満たせる数をしっかりと生産することを最重要とする方針を伝えていた。半導体部品の不足といった状況も解消されているそうで、部材の不足等が生産に影響を与えることもないと考えられているそうだ。マイニンテンドーストアではまずは条件付きの予約抽選受付がおこなわれているものの、発売後には供給が行き渡っていくことも期待される。

「Nintendo Switch 2」は6月5日に発売予定。日本国内向けの予約抽選受付については、4月16日11時までマイニンテンドーストアにて実施中だ。また全国のゲーム取扱店やオンラインショップでは、4月24日以降準備が整い次第、順次予約または抽選の受付が開始されるとのこと。



Source link

Views: 0