演習課題「有理数二分探索」
引数の値によって、その値が大きければ 1, 小さければ -1, ちょうどよければ 0 を返す関数 f が与えられます。
この関数を二分探索して、f(x) = 0 となるような x を、約分された分数の形で求めてください。
右側のコードエリアには、入出力のためのコードがすでに記述されています。このコードにアルゴリズムを実装し、正しく答えを求めてください。
期待する出力値
1/1
#06:有理数二分探索
このチャプターでは、分数を二分探索するアルゴリズムについて学習します。
・探索範囲 l, r をそれぞれ 0/1, 1/0 で初期化
・以下の処理を繰り返す
・1. 中間値 m を l と r の分母同士、分子同士を足し合わせた分数とする
・2. f(m) に応じて分岐する
・2.1. m が求めたい値より小さい場合は、l を m に更新
・2.2. m が求めたい値より大きい場合は、r を m に更新
・2.3. m が求めたい値と一致した場合は、探索を終了
・以下の 3 つの性質が成り立つ
・A. 0 以上のすべての分数をちょうど 1 回ずつ生成する
・B. 生成される分数は 2 つの分数の間の分数である
・C. 生成される分数は既約分数である
・具体的な証明については、https://www.cut-the-knot.org/blue/Stern.shtml (英語) などを参照
・上に述べたアルゴリズムでは、答えが大きな整数やその逆数の場合には、答えの大きさに比例して時間計算量が増えてしまう(スターン・ブロコット木の左端には 1/n, 右端には n/1 が並んでいる)
・これは左右どちらか同じ方向に進み続ける場合に遅くなることが原因である
・そこで、同じ方向に何回進むかを二分探索することで、各ステップでの計算量を O(T log M) に抑えることができ、向きを変えるごとに分子分母の和はほぼ 2 倍以上になるため、全体で O(T log^2 M) に改善される(M は答えの分母分子の最大値、T は関数 f(x) の計算量)