二分探索について、chokudai氏とbeepcap氏との議論をまとめてみた

二分探索はアルゴリズムとしては定番のものですが、その実装方法には色々な流派があります。 これは、その良し悪しについての議論です。
2

発端

古寺いろは@競プロ応援アカウント @codera_iroha

【アルゴリズム雑談】 答えが小数になる二分探索の問題についてですよ! 難易度:★★★☆☆ pic.twitter.com/pxQCvXufgl

2016-05-09 19:39:20
拡大
拡大
拡大
拡大

画像を読むのが面倒な人向けにまとめるとこうなります。

  • double型(に限らず浮動小数点数型)の値で二分探索を行いたい
  • その際、本来なら収束条件として絶対誤差か相対誤差で判定する
  • だが、double型は相対的な差が1e-16程度の単位でしか動かせないため、絶対誤差だと収束しない可能性がある
  • そこで相対誤差による判定を行うのが良いが、ゼロ除算のリスクも一応ある
  • したがって、あえて100回程度で反復回数を決め打ちするのがセオリーである

ですが、これについての反論があったことから、議論が始まりました。

beepcap @beepcap

えーーーー 定数回のloop処理は悪手だし、そもそも固定小数点数を使えよ・・・

2016-05-09 19:45:16
chokudai(高橋 直大)@AtCoder社長 @chokudai

@beepcap 固定小数点数だとどう解決するんですか!定数回のloop処理だとどう悪手なんですか!

2016-05-09 19:48:39
beepcap @beepcap

@chokudai 固定小数点数ならそもそも切り捨てだから振動しませんし、固定回数のループはループを抜けてきた時に振動が発生しているのか、それともループ回数が不足していたのかを別途判定する(しかも、最悪またループしなきゃ!)すひつようがある時点でナンセンスです。

2016-05-09 19:51:10

振動しない=収束する、ということ?

chokudai(高橋 直大)@AtCoder社長 @chokudai

@beepcap 「別途判定が必要になる」というのであれば、あらゆる処理が「十分であるかどうか判定する必要がある」ということになります。競プロのように(当然競プロ以外でも)、値域が与えられている問題に対し、「別途判定を書かなきゃいけない」と考えるほうがおかしいです。

2016-05-09 19:55:34
beepcap @beepcap

@chokudai ああなるほど、実問題ではなく競技プログラミングだから使えるテクニックということですか。なるほど、これは僕の観点が間違っていました。

2016-05-09 19:59:00
chokudai(高橋 直大)@AtCoder社長 @chokudai

@beepcap 競プロ以外では、処理すべてにおいてどんな値域の数が与えられるかまったくわからない、と主張されるのであればその通りです。(当然自分はそのつもりで書いてないですけどね)

2016-05-09 20:00:53
beepcap @beepcap

@chokudai いいえ、そういう意味で言っていません。しかし、ある式が収束しうるかは、入力値の範囲のみでは分かりかねると言っています。

2016-05-09 20:03:22
chokudai(高橋 直大)@AtCoder社長 @chokudai

@beepcap 要求する絶対誤差がaで、返す値としてありうる値の最大値と最小値の差がbとすると、二分探索の終了条件はおおよそlog2(b/a)回ですよー。(まぁ、さっき言った通り、精度の問題があるので理論値ですが) 相対誤差の場合も同様に求められます。

2016-05-09 20:07:36
beepcap @beepcap

@chokudai ああいや、その話は理解できますよ。しかし、終了条件が何で抜けてきたかを判断出来なければ駄目ではないの?という疑問です。

2016-05-09 20:09:14
chokudai(高橋 直大)@AtCoder社長 @chokudai

@beepcap ん?そうなんですか?「これは絶対誤差で抜けてきたからこうして、これは相対誤差で抜けてきたからこうする」ってのがわからないとダメ、って話ですか?そうではなく?

2016-05-09 20:10:19
beepcap @beepcap

@chokudai そうではないのですが、相対誤差、絶対誤差かんけいあります? 僕は単に複数の終了条件で同じ処理に入るときに、どの終了条件かを判定出来ないケースがないかと言っているつもりなのですが。

2016-05-09 20:14:54
chokudai(高橋 直大)@AtCoder社長 @chokudai

@beepcap あ、複数の終了条件の具体例(今回の場合は元ツイートに合わせた)つもりです。判定できないケース、というか、判定するのが必要であれば、まぁそれを返すように組むべきでしょうね。見たことないですけど。

2016-05-09 20:16:06
beepcap @beepcap

@chokudai うーん?それはつまりあの例の関数は「振動している事が分かれば十分」ということですか?

2016-05-09 20:17:29
chokudai(高橋 直大)@AtCoder社長 @chokudai

@beepcap まぁ「どっちかの条件を満たした」ってのが分かれば十分、という認識です。(数が変わらないのって振動っていうんですかね?) 正直何が言いたいかわからないのでお手本コード見せてもらえますか><

2016-05-09 20:20:28
beepcap @beepcap

@chokudai えーっと、double型で2分探索を行う場合という前提がもう間違ってて、固定少数点数を使えよという話なので、前提はdoubleじゃなくていいですかね?

2016-05-09 20:24:35
chokudai(高橋 直大)@AtCoder社長 @chokudai

@beepcap まぁ、要求仕様によっては、多倍長を使った時点で十分な速度が出ず、固定小数点数を使った時点で十分な精度が出ないと思いますので、そこが本質ではないと思いますけど、別にいいですよー。

2016-05-09 20:25:31
beepcap @beepcap

@chokudai 言いたい事は言ってしまった気もするので、こういうことで。 (ビルドも通してないけど) pic.twitter.com/i5MYxT4KCW

2016-05-09 21:00:31
拡大
beepcap @beepcap

なんだろう、100って決め打ちにすると、探索が終わらないうちに処理が終わる危険性が無いかって話が、なんでこじれるのだろう。

2016-05-09 21:02:08
chokudai(高橋 直大)@AtCoder社長 @chokudai

@beepcap あ、もしかして要求誤差が1e9ではなく1e-9であることがかみ合っていない・・・?

2016-05-09 21:07:23
beepcap @beepcap

@chokudai いや多分違うんですけどなんだろうなぁ・・・多分最初から「深度から考えて十分な値を回数として与える」というコードになっていたら、きっとなんかなんにも気にならなかったような気もします。

2016-05-09 21:10:02
chokudai(高橋 直大)@AtCoder社長 @chokudai

@beepcap ぶっちゃけ、突っ込まれポイントとしては、「いやそこは100じゃなくてMath.Log(2, b-a)+2くらいにしとけよ、とか言われたら、「確かに定数だと無駄生じるけど、そこの計算のミスのが怖いから定数にしてるけど、まぁ厳密に書けたほうが良いよね」とは(続き)

2016-05-09 21:11:29