現在のアルゴリズムは正しくありませんが、それにかなり近づいています。ここで、アルゴリズムがどこでうまくいかないかを示します。
配列を考えてみましょう[3,2,1,2]
。
最初に電話するとします。FindMin(F, 0, 3)
FindMin(F, 0, 3)
--ミッド = 1
-- F[1] False をチェック
-- F[1] True をチェックし、FindMin(F, 1, 3) を呼び出します。
----ミッド = 2
----F[2] True をチェックし、FindMin(F, 1, 2) を呼び出します
------ミッド = 1
------チェック F[1] False
------F[1] True を確認し、FindMin(F, 1, 2) を呼び出します
--------ミッド = 1
--------チェック F[1] False
--------F[1] が真であることを確認し、FindMin(F, 1, 2) を呼び出します
...これは、メモリがなくなるまで永遠に続きます
少し変更して正しくすることができます:
FindMin(F, lo, hi){
if(lo==hi) return F[lo];
int mid = lo + (hi-lo)/2 // Actually you can change this into (lo+hi)/2
if(F[mid] > F[mid+1]) return FindMin(F, mid+1, hi) // Change the comparison and recursive call!
if(F[mid] > F[mid-1]) return FindMin(F, lo, mid-1) // Change the comparison and recursive call!
// If we reach here, that means F[mid-1] > F[mid] < F[mid+1]
return F[mid]
}
@Joniが言ったように、境界ケースを処理する必要があります。チェックするだけでF[mid+1]
うまくいきます。この次のコードは、範囲外のエラーが発生せず、正しいことを保証します。
FindMin(F, lo, hi){
if(lo==hi) return F[lo]; // Line 1
int mid = (lo+hi)/2 // Line 2
if(F[mid] > F[mid+1]) return FindMin(F, mid+1, hi) // Line 3
else return FindMin(F, lo, mid) // Line 4
}
hi
配列の最後のインデックスとして関数を呼び出します
ライン 1 は基本ケースです。
行 2 は、中間インデックスを計算します。mid < hi
ここで、は 1 行目で既に説明されているので、 をmid == hi
意味することに注意してください。だから安心してチェックできるlo == hi
mid
F[mid+1]
行 3 は、ある数値よりも大きく、さらに大きくなる可能性があるため、 である場合、 は答えではないF[mid] > F[mid+1]
かどうかをチェックします。したがって、答えは にある必要があります。だから電話してください。であるため、範囲は よりも小さいことに注意してください。F[mid]
F[lo..mid-1]
F[mid+1..hi]
FindMin(F, mid+1, hi)
mid+1 > lo
mid+1..hi
lo..hi
4 行目: ここまで来ると、つまりF[mid] < F[mid+1]
. したがって、答えは のどこにでもあり得ますF[lo..mid]
。だから電話してFindMin(F, lo, mid)
ください。mid < hi
以降、FindMin(F, lo, mid)
とは異なることに注意してくださいFindMin(F, lo, hi)
。より具体的には、3 行目の場合のように範囲が減少します。
ライン 3 とライン 4 を組み合わせると、 への各呼び出しFindMin
は減少する範囲で行われるため、アルゴリズムはライン 1 でしばらくすると停止するはずです。