弾性衝突

問題

$ N $ 個の半径 $ R $ センチメートルのボールを使った次のような実験を行います。

上空 $ H $ メートルのところに筒を設置し、ボールを縦にいれます。

(下から $ i $ 番目のボールはその下端が $ H + 2Ri $ にあります。)

実験開始と同時に一番下のボールを落下させ、以後一秒ごとに1つずつボールを落下させます。

空気抵抗などはなく、床やほかのボールとは弾性衝突をします。

この実験の開始後、$ T $ 秒経過時点での各ボールの下端の高さを求めてください。

ただし、重加速度は $ g = 10m/s^2 $ とします。

[条件]

🔹$ 1 \leqq n \leqq 100 $
🔹$ 1 \leqq h \leqq 10000 $
🔹$ 1 \leqq r \leqq 100 $
🔹$ 1 \leqq t \leqq 10000 $

解法・ソースコード

まずは、ボールが1つだけの場合を考えます。

これはただの物理の問題となり、高さ $ H $ から落下するのにかかる時間は下記の通りです。

$$ t = \sqrt{\frac{2H}{g}} $$

したがって、時刻 $ T $ での位置は $ kt \leqq T $ となる最大の $ k $ に対して次のようになります。

🔹kが偶数の場合: $ y = H - {\frac{1}{2}}g(T - kt)^2 $

🔹kが奇数の場合: $ y = H - {\frac{1}{2}}g(kt + t - T)^2 $


次にボールが複数の場合を考えます。

半径 $ R $ が0と仮定します。全てのボールを同一視すると、ボール同士の衝突を無視して全てすり抜けたと見なすことができます。

衝突によってボールの順序関係は変化しないので、衝突を無視して計算した後で座標をソートすることにより、各ボールの最終的な位置を求めることができます。

また半径 $ R $ が0より大きい場合は、最終的な座標に $ 2Ri $ を足すだけとなります。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#--------- 入力例1 ----------
n = 1 # ボールの個数
h = 10 # ボールの高さ(メートル)
r = 10 # 半径(メートル)
t = 100 # 経過時間(秒)
#--------- 入力例2 ----------
# n = 2 # ボールの個数
# h = 10 # ボールの高さ(メートル)
# r = 10 # 半径(メートル)
# t = 100 # 経過時間(秒)
#----------------------------
import math
g = 10.0 # 重加速度

# 時刻 T でのボールの位置
def calc(t):
if t < 0:
return h
t1 = math.sqrt(2 * h / g)
k = int(t / t1)
if k % 2 == 0:
d = t - k * t1
return h - g * d * d / 2
else:
d = k * t1 + t1 - t
return h - g * d * d / 2

# 各ボールごとの最終位置を算出する
lst = [calc(t - i) for i in range(n)]

for x in sorted(lst):
print('{:.2f}'.format(x), end=' ')
print()

[実行結果(入力例1)]

4.95 

[実行結果(入力例2)]

4.95 10.00