X で見かけたこの映像。とても不思議。
コードで再現したものが以下。コマが回っているように見える。
一方で、色をつけてかつ軌跡がわかるようにすると、振動しているだけだとはっきりわかる。不思議。
この小さな円に見えるものは、式にすると以下のようになる。ここで $t$ は時間を表現した実数。
$$
\left(x- \frac12 \cos t \right)^2 + \left(y + \frac12 \sin t \right)^2 = \frac14.
$$
これはすなわち $(x, y) = (\frac12 \cos t, - \frac12 \sin t)$ を中心に持つ半径 $\frac12$ の円。
これを $x$ 軸で切り取った断面の様子を見る。すなわち $y=0$ を代入する。すると、 $x$ に関する簡単な2次方程式が出てきて、その解は
$$
x = 0, \cos t
$$
である。
特に $x = \cos t$ であることから、$x$ 軸だけに注目すると点が単振動していることがわかる。
この推論は原点を中心とした回転の操作に対して同様に成立する。 $x$ 軸から $\theta$ だけ回転した軸上の点は以下のように表現できる:
R_{\theta}
\begin{pmatrix}
\cos (t + \theta) \\
0
\end{pmatrix}
, ただし $R_{\theta}$ はベクトルを反時計回りに $\theta$ だけ回転させる回転行列。
実際代入してみるとわかるけれども、任意の $t$ と $\theta$ に対して、この点が上で示した小円の円周上に存在することがわかる。
上で作った gif は、 $\theta$ を 0 から $2 \pi$ の間で 8 ほどピックアップし、この式に基づいてプロットした点を $t$ を変化させながら作ったアニメーション。以下がそのコード。
# %%
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
SPLIT_NUM = 8
def rotation_matrix(theta):
return np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]])
def calculate_trajectory(t, theta):
initial_vector = np.array([np.cos(t + theta), 0])
rotated_vector = np.matmul(rotation_matrix(theta), initial_vector)
return rotated_vector
fig, ax = plt.subplots(figsize=(6, 6))
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)
points = []
# Set initial values with t = 0
thetas = np.linspace(0, 2 * np.pi, SPLIT_NUM)
for i, theta in enumerate(thetas):
(point,) = ax.plot(*calculate_trajectory(0, theta), "ro")
points.append(point)
# # Draw line through origin for each theta
x_line = 1.5 * np.cos(theta)
y_line = 1.5 * np.sin(theta)
ax.plot([-x_line, x_line], [-y_line, y_line], color="black", lw=0.5)
ax.grid(False)
for spine in ax.spines.values():
spine.set_visible(False)
ax.set_xticks([])
ax.set_yticks([])
def update(frame):
t = frame / 20.0
for i, theta in enumerate(thetas):
color = "red" if i == 3 else "blue"
color = "blue"
x, y = calculate_trajectory(t, theta)
points[i].set_data(x, y)
points[i].set_color(color)
ani = FuncAnimation(fig, update, frames=range(1000), interval=20)
ani.save("vib_flat.gif", writer="pillow")
おわり。