collatzza
@collatzza

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

有識者求む、助けてください。

解決したいこと

AtCoderでAtCoder Beginner Contest343 に参加したのですが、わからないところがあるので教えていただけると幸いです。

問題はこちらです。
これに対して僕が提出したコードは以下の通りです。

main.py
from math import cbrt, floor
n = int(input())              # 入力受け取り
cn = floor(cbrt(n))           # 入力の立方根の整数値をとる
s = 0
for i in range(cn, 0, -1):    # cnから1まで逆順でfor文を回す
  j = i ** 3
  rj = int(str(j)[::-1])
  if j == rj:                 # 回文になるかどうか試す
    s = j                     # 回文ならsに代入し、forを終わらせる
    break
print(s)                      # 出力

ここでしたいことは、
1からnまでfor文ですると長いけど、nまでの立方数のみを回文かどうか試せばいい。
⇒cnで立方根の整数値をとり、forでcnまでのiの3乗で回文になる立方数を探す。
⇒目的は最大値だから逆順にforを回して回文になったらすぐやめる。
ということです。

認識が間違ってるのか、認識はあってるけどコードが間違ってるか、全部違うか教えてください。
解説やほかの人の答えも見ましたがよくわかりませんでした。

0

2Answer

「認識はあってるけどコードが間違ってる」が一番近いでしょうか。日本語で説明している「ここでしたいこと」を完璧に実装できれば正しく動作するという意味では。

誤答となる入力例も挙げておきます。

1000300030001

正しい出力は入力と同じく1000300030001になります。(10001 ** 3 == 1000300030001 なので)

誤答の原因はfloatの誤差です。
実際に計算させてみると、本来10001となるべきなのに、誤差で少し小さい値になるのが分かります。

>>> math.cbrt(1000300030001)
10000.999999999998

こちらの問題の制約に、以下の記述があります。

  • $N$は$10^{18}$以下の正整数

ここで、$10^{18}-64$をfloatに変換してみましょう。

>>> float(10 ** 18 - 64)
1e+18
>>> float(10 ** 18 - 64) == 10 ** 18
True

なんと、$10^{18}$と完全に等しくなってしまいました。
つまり、$10^{18}-64$という数はfloatでは表現できず、$10^{18}$に丸められてしまうのです。

このように、floatではこの問題の制約を満たす整数を表現しきれません。よって、floatで計算を行うのはまずいということが、問題の制約からも分かります。


競プロでは、floatを使うとWAになるようなテストケースが仕込まれていることがあるので、最初からfloatを使わない前提でコードをくみ上げる方が無難です。昔、あからさまにmath.logでの計算を狙い撃ちしたようなテストケースを見たことがあります。(以下参照)


最後に、原因が誤差であることを示すため、「ここでしたいこと」を誤差なしで実装してACになったコードを載せておきます。

1Like

Comments

  1. 他回答へのコメントにある質問への回答を、元回答に追記しています。「誤答の原因は」以降が追記箇所になります。

以下の変更でACできます

from math import cbrt, floor
  n = int(input())              # 入力受け取り
- cn = floor(cbrt(n))           # 入力の立方根の整数値をとる
+ cn = floor(cbrt(n)) + 1       # 入力の立方根の整数値をとる
  s = 0
  for i in range(cn, 0, -1):    # cnから1まで逆順でfor文を回す
    j = i ** 3
+   if j > n: continue
    rj = int(str(j)[::-1])
    if j == rj:                 # 回文になるかどうか試す
      s = j                     # 回文ならsに代入し、forを終わらせる
      break
  print(s)                      # 出力
0Like

Comments

  1. @collatzza

    Questioner

    @nak435 さん、
    ありがとうございます!そのコードでできました!
    私のコードは浮動小数点数の誤差が生じるので一部間違う点が出てくるという認識でいいんでしょうか?

  2. +1しましたが、もしかしたら、ceilで切り上げれば+1は不要かも知れません(検証はしていません)。

    なお、制約を前提にすれば、立方根で上限を決めることもなく、無条件で $10^6$でよいですね。

Your answer might help someone💌