応用情報技術者 2011年 秋期 午前2 問08
問題文
データが昇順にソートされた配列Xiを2分探索する。流れ図のaに入るものとして、適切なものはどれか。ここで、流れ図の中の割り算は小数点以下を切り捨てるものとする。

選択肢
ア:left < right
イ:left ≦ right(正解)
ウ:left + 1 < right
エ:left + 1 ≦ right
🔒 解説は解答すると表示されます
二分探索のループ条件【午前2解説】
正解の理由
探索ループは探索値が見つかっていない(sch == −1)かつ探索対象の範囲が存在する間に継続する必要があります。配列の有効範囲を左右のインデックスで表すと、要素が存在するのは のときです。したがって、選択肢イの条件「left ≦ right」をループ継続条件に用べきです。これにより、範囲が1要素のとき()でもその要素を調べる反復が行われ、見落としが生じません。
(流れ図では sch が見つかったら sch に index を格納してループを終わらせるため、ループ条件は (sch == −1) と (left ≦ right) の論理積になります。)
解法ステップ
- 配列が昇順でソートされているため、中央値 center を計算して比較し、探索範囲を半分にする二分探索の基本に従う。
- ループを継続すべきときは「未発見で、かつまだ探索すべき要素が残っている場合」であり、探索すべき要素が残っていることは で判定する。
- 各反復で center ← (left + right) ÷ 2(小数点以下切り捨て)を計算し、X[center] と探索値を比較する。
- 小さいとき: left ← center + 1
- 等しいとき: sch ← center(探索成功、ループ条件の sch == −1 が false になり終了)
- 大きいとき: right ← center − 1
- ループを抜けたら sch を表示(見つからなければ -1 のまま)。
選択肢別の誤答解説
-
ア: left < right
left < right は「範囲に2以上の要素があるときのみ継続」する条件です。これだと範囲がちょうど1要素()のときループを行わず、その単独要素の比較を行わないため、存在する要素を見逃す可能性があります。従って不適切です。 -
イ: left ≦ right
これは正答です。範囲に1要素ある場合()もループを継続して比較を行うため、正しく動作します。流れ図の中心計算と左右更新(center ± 1)と組み合わせると、探索が適切に収束します。 -
ウ: left + 1 < right
left + 1 < right は right − left ≥ 2、つまり「範囲が3要素以上のときのみ継続」する条件に相当します。これでは範囲が1要素や2要素のときにループを中断してしまい、正しい検索結果を得られません。 -
エ: left + 1 ≦ right
left + 1 ≦ right は right − left ≥ 1、すなわち「範囲が2要素以上のとき継続」する条件です。範囲が2要素のときは継続しますが、範囲が1要素()のときはループを終了してしまいます。したがって単一要素を検査できず、誤検出を招きます。
(注意:左辺に +1 が入る条件は「2要素以上」を判定するものになり、1要素のときは継続しない点が重要です。)
よくある誤解
- 等号の有無を軽視する誤り:while の条件で ≤ を使うべきか < を使うべきか迷い、単一要素の検査を抜かすミスが多いです。二分探索では人為的な off-by-one を招きやすいので注意してください。
- center の算出で切り捨て方を混同:中心計算は整数除算(切り捨て)で行うこと。言語によっては負数の切り捨て挙動が異なるので注意が必要です。
- 中央値計算のオーバーフローを無視:大きな配列で (left + right) が整数の上限を超える場合があります。安全には center ← left + (right - left) ÷ 2 を用いる実装が好まれます。
補足コラム
- ループ条件を while (left <= right) にしておけば、見つかれば即座に sch を更新してループを抜ける設計と自然に合います。別の実装スタイルとして while (left < right) を用いることもありますが、その場合は center の偏り(左寄せ/右寄せ)やループ内の更新法を工夫して、最終的に残る1要素を別途チェックする必要があります。
- 中央値計算のオーバーフロー回避例:center = left + (right - left) // 2
コード例(Python)
def binary_search(X, target):
sch = -1
left = 0
right = len(X) - 1
while sch == -1 and left <= right: # <mark>イ</mark> に対応
center = left + (right - left) // 2
if X[center] < target:
left = center + 1
elif X[center] == target:
sch = center
else:
right = center - 1
return sch
FAQ
Q. while (left < right) と while (left <= right)、どちらが良いですか?
A. 基本形としては while (left <= right) が分かりやすく安全です。while (left < right) を使う場合はループ後に残る 1 要素を別途チェックする実装上の配慮が必要です。
A. 基本形としては while (left <= right) が分かりやすく安全です。while (left < right) を使う場合はループ後に残る 1 要素を別途チェックする実装上の配慮が必要です。
Q. center の計算で (left + right) // 2 と left + (right - left) // 2 の違いは?
A. 結果は同等ですが、前者は left + right が大きくなるとオーバーフローする可能性があります。安全性を考えると後者(差を使う方法)が推奨されます。
A. 結果は同等ですが、前者は left + right が大きくなるとオーバーフローする可能性があります。安全性を考えると後者(差を使う方法)が推奨されます。
関連キーワード: 二分探索、境界条件、中心計算、オフバイワン、探索アルゴリズム

\ せっかくなら /
応用情報技術者を
クイズ形式で学習しませんか?
クイズ画面へ遷移する→
すぐに利用可能!

