グラフが描けたけど、今回はこの話題。
二分探索です。
二分探索、バイナリサーチで求めたデータを掲載する。
今回は0から2までを探索するとして、
0と2を分母が1の分数で
0/1, 2/1
と表します。
これらの中間は、
分母を揃えて、分子を足して、分母を2倍する。
今回は分母が等しいので、分子を足して0+2=2、分母を2倍して2
2/2=1/1
これが中央です。
グラフをみて分かる通り、
x=0/1では振動して解の個数が2
x=1/1では収束して解の個数が1
x=2/1では発散して解の個数が0
解が2と1の境界はどこ?
解が1と0の境界はどこ?
と2つの境界を探したいわけでした。
0/1と1/1の中間は、1/2では解の個数は1
0/1と1/2の中間は、1/4では解の個数は1
0/1と1/4の中間は、1/8では解の個数は1
0/1と1/8の中間は、1/16では解の個数は2
1/16と1/8の中間は、3/32では解の個数は1
1/16と3/32の中間は、5/64では解の個数は1
1/16と5/64の中間は、9/128では解の個数は1
1/16と9/128の中間は、17/256では解の個数は1
1/16と17/256の中間は、33/512では解の個数は2
33/512と17/256の中間は、67/1024では解の個数は2
67/1024と17/256の中間は、135/2048では解の個数は2
135/2048と17/256の中間は、271/4096では解の個数は1
135/2048と271/4096の中間は、541/8096では解の個数は1
135/2048と541/8096の中間は、1081/16384では解の個数は2
…
1/1と2/1の中間は、3/2では解の個数は0
1/1と3/2の中間は、5/4では解の個数は1
5/4と3/2の中間は、11/8では解の個数は1
11/8と3/2の中間は、23/16では解の個数は1
23/16と3/2の中間は、47/32では解の個数は0
23/16と47/32の中間は、93/64では解の個数は0
23/16と93/64の中間は、185/128では解の個数は0
23/16と185/128の中間は、369/256では解の個数は1
369/256と185/128の中間は、739/512では解の個数は1
139/512と185/128の中間は、1479/1024では解の個数は1
1479/1024と185/128の中間は、2959/2048では解の個数は0
1479/1024と2959/2048の中間は、5917/4096では解の個数は1
5917/4096と2959/2048の中間は、11835/8192では解の個数は0
5917/4096と11835/8192の中間は、23669/16384では解の個数は1
…
手作業でこれくらいをやっていると、求める分数のルールというか、アルゴリズムが見えてくるのではなかろうか?
a/bとc/dの中間の求め方、
a/b < c/d
と大小関係を崩さずに書いているとおり、
小さい方の分子aから中間の分子を求めるには、2a+1
大きい方の分子cから中間の分子を求めるには、2c-1
分母はbとdを比べて、大きい方を2倍したものが中間の分母
というアルゴリズムが見えてくる。
わざわざ、
分母を揃えるために、
bとdの最小公倍数fを求め、
l=lcm(b,d)
fを使って分子を
(f/d)a+(f/b)c
というように求め、
分母;を2倍して、
((f/d)a+(f/b)c)/(2f)=e/(2l)
が中間値
といった手順を踏んでやり続けてはいない。
さて、コンピュータの世界では、なぜ二分探索が持て囃されるのだろうか。
それは、コンピュータの世界が2進数であり、
2倍、半分という計算が、他の計算に比べて格段に速く計算出来るからだと考えます。
二分探索のアルゴリズムで、わかりやすいように分数で書きましたが、
コンピュータの中では、2倍、半分というのは、値を変えずに、小数点の位置をずらしているということに他ならない。
我々人間の世界では、おおよそ10進数が幅を利かせているだろう。
10進数において、10倍や10分の1という作業をするとして、
小数点表記ならば、小数点の位置を、
1つ右へずらせば10倍、
1つ左へずらせば10分の1、
分母分子が整数の分数表記ならば、
分子の末尾に0をつければ10倍、
分母の末尾に0をつければ10分の1、
である。
計算しているというよりも、操作しているに近い感覚である。
最小公倍数を求めるとか、かなり面倒なんです。
かなり面倒ではあるからこそ、最適なアルゴリズムというものを研究する価値はあるんです。
最大公約数を求めるアルゴリズムには、ユークリッドの互除法というものがあり、最小公倍数は最大公約数を求め、そこから簡単な計算で求まります。
但し、今回の二分探索で、最小公倍数を求める作業自体が不要であるということ。
a/bとc/dの中間値e/fを求めたとして、
今、手にはe/fの値を保持していて、それらを使いやすい状態にあり、
a/bとe/fの中間値g/hを求めたいならば、
a/b < e/f であることは承知しているので、
g=2e-1
h=2f
e/fとc/dの中間値g/hを求めたいならば、
e/f < c/d であることは承知しているので、
g=2e+1
h=2f
中間値e/fは求まって、保持されていて、使いやすい状態である。
という作業を繰り返すのだ。
とにかく、余計な計算を出来るだけ省くこと。
その結果、何をやっているのかが解りにくくなってしまう可能性はあるが、
だからこそ、しっかりと、なぜそのようなことをさせるのか、コメントを付けるなりして明確にしておく必要が出てくる。
式を見てわかることを、わざわざコメントにして残す必要はない。
コメントに残すは、その式の持つ意味や、その式に至る経緯である。
さて、二分探索はどこまで続けていけばよいだろうか。
点Qの二分探索は、
…
5917/4096と11835/8192の中間は、23669/16384では解の個数は1
解は、2.69123598884332760321351489915819822154529295428855
23669/16384と11835/8192の中間は、94677/65536では解の個数は1
解は、2.70050560222594851999857114276102525446725236898031
94677/65536と11835/8192の中間は、378711/262144では解の個数は1
解は、2.71716549500560886112744599408065360658712526608210
もしや、あの数ではなかろうか…
もう少し確証が欲しい…
378711/262144と11835/8192の中間は、757423/524288では解の個数は0
378711/262144と757423/524288の中間は、1514845/1048576では解の個数は0
378711/262144と1514845/1048576の中間は、3029689/2097152では解の個数は0
378711/262144と3029689/2097152の中間は、6059377/4194304では解の個数は0
378711/262144と6059377/4194304の中間は、12118753/8388608では解の個数は0
378711/262144と12118753/8388608の中間は、24237505/16777216では解の個数は0
378711/262144と24237505/16777216の中間は、48475009/33554432では解の個数は1
2.71763515128764447183281313980324635434659322505466
やっぱりあの値や、
e≒2.71828182845904523536028747135266249775724709369995
これはネイピア数に近づいていってるんじゃなかろうか。
他もネイピア数絡みだろう。
48475009/33554432=1.4446678459644317626953125
これも、
e^(1/e)≒1.44466786100976613365833910859643022305859545324225
これだろう。
点Pの二分探索は、
…
135/2048と541/8096の中間は、1081/16384では解の個数は2
0.37429215976509466303587037208369240585913284724856
0.36150376945126193455655505051387072637197087694885
1081/16384=0.06597900390625
1081/16384と541/8096の中間は、2163/32768では解の個数は1
0.36790147175139879157552022747348609693545932610938
2163/32768=0.066009521484375
ネイピア数絡みということで、
1/e≒0.36787944117144232159552377016146086744581113103176
e^(-e)≒0.06598803584531253707679018759684642493857704825279
P(e^(-e),1/e)
Q(e^(1/e),e)
ではなかろうか。
コンピュータの性能が高ければ、二分探索をもっと繰り返すことで、より近づいていくことを確認出来るだろうが、まぁ時間も掛かるんで、私はこのくらいでやめました。
先のグラフの画像は、HTML5+Javascriptで書いています。
画像の大きさは、800px×800px
ブログに載せるくらいですから、これくらいで十分かと。
x軸、y軸ともに、250pxを1としました。
つまり、いままで求めた値を250倍して、1px以上違うと、グラフとしての精度は良くないけど、
1px未満に収まれば、白地に黒線を引いたとしても、グレーになる感じですかね。
緑線の両端、
点Qの座標
e×250≒
679.570457114761308840071867838165624439311773424989
2.71763515128764447183281313980324635434659322505466×250=
679.408787821911117958203284950811588586648306263665
(e^(1/e))×250≒
361.166965252441533414584777149107555764648863310563
48475009/33554432×250=
361.166961491107940673828125
点Pの座標
(1/e)×250≒
91.969860292860580398880942540365216861452782757941
0.36790147175139879157552022747348609693545932610938×250=
91.975367937849697893880056868371524233864831527345
e^(-e)×250≒
16.497008961328134269197546899211606234644262063199
2163/32768×250=
16.50238037109375
このくらいの解像度の画像なら、十分な値かと思われます。
あとは、グラフの曲線がなめらかにみえるくらいの精度で、直線として繋いだだけです。
ではでは