上限が不明なときの問題解決法

例えばProblem 60のような条件を満たす最小の解を求る問題を考えます。この問題は条件を満たすような解を小さい順に列挙するのが難しいです。しかし、上限を決めてその範囲で解が存在するかを含めて解くのは比較的簡単です。

このようなときは、適当に上限を決めるとよいです。例えば上限を1000と決めて、その範囲に解が無ければ、上限を2倍にして再び解を求めます。この方法は多くの場合にかなり有効です。上限を決めると問題がぐっと易しくなることはよくあります。また、直接解を求めるより時間がかかりそうですが、致命的にはかかりません。例えば、O(N)の問題のとき、解が8000で、いきなり上限を8000にして解を求めるたとき8秒かかったとします。ことのき、N = 1000から始めて倍々していくと、1 + 2 + 4 + 8 = 15秒かかります。まあ、倍くらいです。最悪の場合、N = 999から始めて倍々していくと、1 + 2 + 4 + 8 + 16 = 31秒と4倍くらいかかります。

しかし、これはいつも有効でしょうか。例えばO(N3)の問題について考えます。すぐにわかりますが、最悪の場合は最善の場合の8倍の時間がかかります。解が8000のとき、N = 1000から倍々して8000のときで終わりますが、N = 999からはじめたときは、7992で終わらずに15984まで計算しなければなりません。このとき約8倍かかることになります。

これを避けるには、倍々ではなくもっと小さい係数を掛けていけばよいでしょう。しかしあまり小さい係数を掛けていては余分な計算をたくさんすることになるでしょう。どこかに最適な係数があるはずです。それを求めます。

定式化します。計算量がO(Ne)、解がM = an + 1、N = 1から始めてa倍ずつしていきます。そして、an + 1まで計算するとします。このときの計算量はおおよそ、

 \frac{a^{e(n+2)}}{a^e - 1} \approx \frac{M^ea^{2e}}{a^e - 1}

これをaで微分したものが0になるとして、

 2ea^{2e-1}(a^e - 1) = a^{2e}ea^{e-1}
 a^e = 2

すなわち、

 a = 2^{1/e}

となります。O(N3)なら、a = 1.26倍ずつしていくとよいことになります。


分かりにくいのでグラフを書いてみました。上限を100から始めて2倍ずつして解に対する計算量を求めています。それと1.26倍と比較しています。

対数表示なのでわかりにくですが、2倍の方がステップが大きいことはわかると思います。その分最悪のときに計算量が多くなります。。ともにフラットな解が7000くらいのところで2.4倍くらい2倍ステップの方が計算量が多くなっています。

Project Eulerに必要な用語集