CodeIQで出題された「ロング・ロング・ストリング」問題。
自分もRubyで解いたので、コードを公開してみる。
問題
問題は、以下のとおり:
自然数 に対して、関数 を、( の 乗)を 10 進数で表したときの桁数と定義します。
例えば、 ですので、 です。同様に、 です。
さらに、2 以上の自然数 に対して、 となる の値を と定義します。
もしそのような が存在しない場合は、 と定義します。
例えば 、 です。同様に、、、 となることが確かめられます。
標準入力から、自然数 が与えられます。
標準出力に の値を出力するプログラムを書いてください。
解説は以下で公開されている。
https://codeiq.jp/magazine/2016/03/38398/
自分の解答
自分の考えは、以下のとおり:
まず、 は を10進数で表したときの桁数なので、次のように書くことが出来る:
なので、与えられた自然数 について、次の等式を満たす自然数 を見つければいいと分かる:
ただ、このままだと床関数が面倒なので、次のように変形:
あとはこの不等式を満たす自然数 を見つければいいんだけど、単に1から探していくのだと、効率が悪い。
ということで、こういうときの定番手法であるバイナリサーチを使うことにした。
バイナリサーチを使う場合、下限と上限が必要で、とりあえず下限は1と分かっているので、まずは下限を更新しつつ、上限を探すところから。
- 最初に とする。
- 以下を繰り返す:
- なら、 はまだ小さいので、下限を で更新し、とする。
- なら、 は十分大きいので、上限を とし、ループを抜ける。
ちょっと注意が必要なこととして、 のとき、同時に が成り立っていたら、条件を満たす が見つかったことになるので、そこで検索自体が終了になる。
これで上限が見つかったら、あとは普通にバイナリサーチ。
- 以下を繰り返す:
- を下限と上限の中間の値にする。(小数点以下は切り捨て)
- を満たしていたら、 を返す。
- そうでない場合、以下のように下限/上限を更新:
- なら、下限を で更新する。
ただし、下限と が等しかったら、-1を返す。 - なら、上限を で更新する。
- なら、下限を で更新する。
ちなみに、上限を更新するときに終了のチェックをしていないのは、上限が と等しくなることはありえないから。
というのも、上限が と等しくなり得るのは、(小数点以下を切り捨てることから)下限と上限が等しいときのみで、下限は常に を満たすように更新しているので、 とはなり得ないから。
以上をコードに落とすと、以下の通り:
include Math def g(m) n = 1 lower_bound = 1 upper_bound = nil loop do value = n * log10(n.to_f) if (m - 1) <= value if value < m return n else upper_bound = n break end else lower_bound = n n *= 2 end end loop do n = (upper_bound + lower_bound) / 2 value = n * log10(n.to_f) if ((m - 1) <= value) && (value < m) return n end if value < (m - 1) if lower_bound == n return -1 else lower_bound = n end else upper_bound = n end end end m = $stdin.gets.to_i puts g(m)
※シンタックス・ハイライトすると、 の数式がおかしくなるので、シンタックス・ハイライトなし
解説のコードと比べると、最初に上限を探しにいってるので、その分、ちょっとややこしいかも。
今日はここまで!