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)
※シンタックス・ハイライトすると、 の数式がおかしくなるので、シンタックス・ハイライトなし
解説のコードと比べると、最初に上限を探しにいってるので、その分、ちょっとややこしいかも。
今日はここまで!