概要
以下のような問題を解いていた。
1000m先に信号機がある。この信号は、15秒ごとに赤になり、また15秒後に緑に戻ることを繰り返す。
ある自動車がこのエリアにs km/hで突入し定速で走り続けたが、この信号を緑で通過したい。
0<= s <= 200とするとき、これを実現できる最大の速度を求めよ。ただし、sは整数とする。
尚、信号はこの地点に到達した瞬間に緑であれば通過できるし、赤であれば通過できないとする。
この信号はエリアに入った瞬間に緑になったものとする。
# 考え方
まず、ある信号機のある地点についた時間tが$ 0 \leqq t \lt 15$や$ 30 \leqq t \lt 45$, $ 60 \leqq t \lt 75$であれば通過でき、$ 15 \leqq t \lt 40$や$ 45 \leqq t \lt 60$であれば通過できない。つまり、tを15で除算した結果を切り下げたとき、偶数であればOK(通過), 奇数であればNG(通れない)となる。
次に、スピードは時速(km/h)であり、信号機の地点は(m)であるので単位を合わせる必要がある。
求めたいのは200km/h以下でこの信号機を通過できるかなので、200->1までforで回して通過できる条件を満たせばよい。これをシミュレートする。
d = 1000
for s in range(70, 0, -1): #70km/h -> 1km/hまでシミュレート
# 信号までの距離[m] を 速度(秒速かつm単位)で割る
if ( ( d // (s / 3600 * 1000)) ) % 15 == 0:
print("[1]OK! s = {0}".format(s))
break
# 上の式を変形したもの
for s in range(70, 0, -1):
if ( ( d * 3600 / 1000) // s ) % 15 == 0:
print("[2]OK! s = {0}".format(s))
break
"""
[1]OK! s = 40
[2]OK! s = 60
"""
[1]と[2]は式を変形したもので、結果は同じ値でないとおかしいが、なぜか結果が異なる。
\frac{d}{\frac{s}{3600} 1000} \Leftrightarrow \frac{d \frac{3600}{1000}}{s}
手で計算すると、例えばs=60のとき、
\frac{1000}{\frac{60}{3600} 1000} = \frac{3600}{60} = 60
となり、$60 / 15 = 4$で偶数のため通過できるのが正しい。式をPython3で計算すると、
[1]>>> (1000 // (60 / 3600 * 1000))
59.0
[2]>>> (1000 * 3600 / 1000) // 60
60.0
となり結果が一致しない。なぜこうなるかというと、
[1]の式の一部
>>> 60/3600
0.016666666666666666
>>> 60/3600*1000
16.666666666666668
>>> a=60/3600*1000
>>> 1000 / a
59.99999999999999
というように、60/3600を1000で倍にして無限小数が丸められた結果、結果が60にならず、59.999となってしまったからである。
例えば
>>> 1000 / (60 / 3600) / 1000
60.0
のようにしても、正しく答えは得られる。
まとめ
無限小数を乗算すると、まるめが出てしまうので注意しなければならない。