from cmath import exp
from math import pi
fizz = 1
buzz = 1
for i in range(1, 101):
fizz *= exp(2j*pi/3)
buzz *= exp(2j*pi/5)
fizz_is_1 = abs(fizz - 1) < 0.0001
buzz_is_1 = abs(buzz - 1) < 0.0001
if fizz_is_1 and buzz_is_1:
print("FizzBuzz")
elif fizz_is_1:
print("Fizz")
elif buzz_is_1:
print("Buzz")
else:
print(i)
え? なにこれ?
$\exp(j\theta)$は、半径1の円を$\theta$回転させた座標です。
また、複素数にこれを掛けることで、さらに$\theta$回転させることができます。
今回、$\exp(\frac{2j\pi}{3})$, $\exp(\frac{2j\pi}{5})$を繰り返し掛けることで、繰り返し$\frac{2\pi}{3}$, $\frac{2\pi}{5}$ラジアンずつ回転させて、周期3および5で1に戻ってくる複素数を作って、Fizz, Buzzの判定に使っています。
なお、この方法は、計算誤差が溜まっていくため、繰り返す数が100回などではなく巨大な数だと破綻すると思われますが、100回程度なら問題ありません。
周期も3や5と、小さいので、回転角が比較的大きく、なのでabs(fizz-1) < 0.0001
と、割と余裕を持って1に戻ってきているかの判定もできています。