Столкновение бильярдных шаров

24

Учитывая двумерные положения и скорости пары бильярдных шаров непосредственно перед ударом, рассчитайте их скорости после идеально упругого столкновения . Предполагается, что шары являются идеальными сферами (или, что то же самое, кругами) с одинаковым радиусом, одинаковой массой, однородной плотностью и без трения.

Вход состоит из 8 чисел: p0x,p0y,v0x,v0y,p1x,p1y,v1x,v1yгде p0x,p0yнаходится центр первого шара, v0x,v0yего скорость и аналогично p1x,p1y,v1x,v1yдля второго шара. Вы можете принимать входные данные в любом порядке и структурировать их любым удобным способом, например, в виде массива 2x2x2 или, возможно, массива 2x2 для pи двух массивов длины-2 для v0и v1. Также хорошо брать комплексные числа (если ваш язык их поддерживает) вместо пар ху. Однако вы не должны принимать входные данные в системе координат, отличной от декартовой, т. Е. Полярная не допускается.

Обратите внимание, что радиус бильярдного шара равен половине расстояния между p0x,p0yи p1x,p1y, поэтому он не указан как явная часть ввода.

Напишите программу или функцию, которая выводит или возвращает 4 числа в любом удобном декартовом представлении: значения после столкновения v0x,v0y,v1x,v1y.

диаграмма столкновений

Возможный алгоритм:

  • найти нормальную линию, которая проходит через оба центра

  • найти касательную, которая проходит через среднюю точку между двумя центрами и перпендикулярна нормальной линии

  • изменяется в системе координат и ломаются v0x,v0yи v1x,v1yв их тангенциальные и нормальные компоненты v0t,v0nиv1t,v1n

  • поменять местами нормальные компоненты v0и v1, сохраняя их тангенциальные компоненты

  • вернуться к исходной системе координат

Тесты (результаты округлены до 5 знаков после запятой):

   p0x   p0y   v0x   v0y   p1x   p1y   v1x   v1y ->      v0x'       v0y'       v1x'       v1y'
[-34.5,-81.8, 34.7,-76.1, 96.2,-25.2, 59.2,-93.3] [  49.05873, -69.88191,  44.84127, -99.51809]
[ 36.9, 77.7,-13.6,-80.8, -7.4, 34.4, 15.1,-71.8] [   5.57641, -62.05647,  -4.07641, -90.54353]
[-51.0, 17.6, 46.1,-80.1, 68.6, 54.0,-35.1,-73.9] [ -26.48927,-102.19239,  37.48927, -51.80761]
[-21.1,-52.6,-77.7, 91.5, 46.0, 94.1, 83.8, 93.7] [ -48.92598, 154.40834,  55.02598,  30.79166]
[ 91.3, -5.3, 72.6, 89.0, 97.8, 50.5, 36.2, 85.7] [  71.73343,  81.56080,  37.06657,  93.13920]
[-79.9, 54.9, 92.5,-40.7,-20.8,-46.9,-16.4, -0.9] [  47.76727,  36.35232,  28.33273, -77.95232]
[ 29.1, 80.7, 76.9,-85.1,-29.3,-49.5,-29.0,-13.0] [  86.08581, -64.62067, -38.18581, -33.47933]
[ 97.7,-89.0, 72.5, 12.4, 77.8,-88.2, 31.5,-34.0] [  33.42847,  13.97071,  70.57153, -35.57071]
[-22.2, 22.6,-61.3, 87.1, 67.0, 57.6,-15.3,-23.1] [ -58.90816,  88.03850, -17.69184, -24.03850]
[-95.4, 15.0,  5.3, 39.5,-54.7,-28.5, -0.7,  0.8] [  21.80656,  21.85786, -17.20656,  18.44214]
[ 84.0,-26.8,-98.6,-85.6,-90.1, 30.9,-48.1, 37.2] [ -89.76828, -88.52700, -56.93172,  40.12700]
[ 57.8, 90.4, 53.2,-74.1, 76.4,-94.4,-68.1,-69.3] [  51.50525, -57.26181, -66.40525, -86.13819]
[ 92.9, 69.8,-31.3, 72.6,-49.1,-78.8,-62.3,-81.6] [-123.11680, -23.48435,  29.51680,  14.48435]
[-10.3,-84.5,-93.5,-95.6, 35.0, 22.6, 44.8, 75.5] [ -11.12485,  99.15449, -37.57515,-119.25449]
[ -3.9, 55.8,-83.3,  9.1, -2.7,-95.6, 37.7,-47.8] [ -82.84144, -48.75541,  37.24144,  10.05541]
[-76.5,-88.4,-76.7,-49.9, 84.5, 38.0,  4.2, 18.4] [   6.52461,  15.43907, -79.02461, -46.93907]
[ 64.2,-19.3, 67.2, 45.4,-27.1,-28.7, 64.7, -4.3] [  59.66292,  44.62400,  72.23708,  -3.52400]
[  9.8, 70.7,-66.2, 63.0,-58.7, 59.5, 83.7,-10.6] [  68.07646,  84.95469, -50.57646, -32.55469]
[ 62.9, 46.4, 85.0, 87.4, 36.3,-29.0,-63.0,-56.3] [  23.53487, -86.82822,  -1.53487, 117.92822]
[ -5.5, 35.6, 17.6,-54.3, -2.2, 66.8,-15.2, 11.8] [  24.15112,   7.63786, -21.75112, -50.13786]

Кратчайшие победы. Нет лазеек.


спасибо @Anush за помощь в исправлении цвета фона диаграммы

СПП
источник

Ответы:

16

Python 3 , 67 66 байт, 53 байта

def f(p,v,q,w):p-=q;d=((v-w)/p).real*p;return v-d,w+d

Попробуйте онлайн!

-1 байт благодаря @ngn

-13 байт благодаря @Neil

Эта функция f принимает четыре комплексных числа в качестве входных данных и возвращает два комплексных числа. Развернутая версия показана ниже.

Ungolfed

def elastic_collision_complex(p1, v1, p2, v2):
    p12 = p1 - p2
    d = ((v1 - v2) / p12).real * p12
    return v1 - d, v2 + d

Попробуйте онлайн!

Формула вычисления выводится на основе формулы 2D-вектора в вики . Поскольку m1=m2 , формулу можно упростить до

{v1=v1dvv2=v2+dv

Пусть x12=x1x2 и v12=v1v2 , мы имеем

dv=v12,x12x122x12=Re(v12x12¯)x12x12¯x12=Re(v12x12¯x12x12¯)x12=Re(v12x12)x12

p12v1 - v2dx12y12dv

Joel
источник
1
отлично сработано! этот подход отличается от ответа Рамили perl6, в котором также используются комплексные числа. вы можете сохранить байт , если заменить r=p-qс p-=qи в дальнейшем использовать pвместо r, как в ответ Нейла расслоение плотной
СПП
1
@ngn, это выглядит иначе, но это то же самое, как правильно замечает Джоэл. Я написал формулу в форме, которая была бы хороша для игры в гольф на Perl 6, и Джоэл предположительно использовал ту, которая была лучше для Python. Во всяком случае, я не думал, что кто-нибудь еще придумает решение, использующее комплексные числа независимо. Отличная работа!
Рамилли
3
Хорошо, но если бы вы использовали алгоритм в вопросе, это заняло бы только 53 байта ...
Нил
1
@Neil Спасибо за подсказку. Вычисления теперь значительно упрощены.
Джоэл
3
Мне действительно нравятся все ваши отличные решения и подробные объяснения!
xnor
11

JavaScript (Node.js) , 90 88 байт

(m,n,o,p,q,r,s,t,u=(q-=m)*q+(r-=n)*r,v=o*q+p*r-s*q-t*r)=>[o-(q*=v/u),p-(v*=r/u),s+q,t+v]

Попробуйте онлайн! Ссылка включает тестовый набор. Пояснение: q,rперенумерованы как разностный вектор между центрами, и uэто квадрат его длины. vРазница в точечных произведениях o,pи s,tс q,r, так v/uчто коэффициент масштабирования для q,rэтого дает количество скорости, передаваемой из o,pв s,t. Редактировать: 2 байта сохранены благодаря @Arnauld.

Нил
источник
я не ожидал, что кто-то так быстро упростит алгоритм, молодец! Вот визуализация вашего решения (с улучшением
Арнаулда
@ngn Неверная ссылка?
Нил
Журнал конвейеров @Neil gitlab говорит, что он должен быть там. Ctrl + f5? стрелки контролируют красный шар. сдвиг ускоряется. проверено в Firefox и хроме. предупреждение: звук.
СПП
@ngn А, работаю сейчас, спасибо! (Я получил 404 раньше. Кроме того, я использовал личную вкладку, поэтому у меня не было звука по умолчанию, хотя я не нашел его навязчивым. И я бесполезен на Астероидах, в противном случае я бы попросил "стрелять" "ключ ...)
Нил
8

Perl 6 ,75 64 63 61 байт

11 байтов сохраняются при переключении с mapна for, без необходимости помещать вещи в промежуточные переменные, чтобы их mapможно было увидеть.

1 байт сохранен путем изменения ($^a-$^c)².&{$_/abs}на ($^a-$^c).&{$_/.conj}.

2 байта сохранены благодаря @nwellnhof.

{(.($^b+$^d,{$_/.conj}($^a-$^c)*($b-$d).conj)/2 for *-*,*+*)}

Попробуйте онлайн!


объяснение

Когда в оригинальном сообщении говорилось, что входные данные могут быть комплексными числами, сопротивляться было слишком сложно ... Так что для этого требуется 4 комплексных числа (позиция 1, скорость 1, позиция 2, скорость 2) и возвращает скорости в виде комплексных чисел.

Программа использует точно такой же алгоритм, как описано в ОП. Однако с комплексными числами это довольно просто. Во-первых, давайте заметим, что комплексное числоdзнак равноп1-п0указывает от первого мяча ко второму. Таким образом, если мы разделим все скорости на него, то нормальное направление внезапно совпадет с действительной осью, а направление касательной - с мнимой осью. (Это портит величины, но нам все равно.)

Теперь нам нужно переключить нормальные (то есть реальные) части скоростей v0/d а также v1/dи после этого умножьте это на d again to make the normal (and the velocities) point in the correct direction (and to unmess the magnitudes). So we need to calculate

v0=d(v1d+iv0d),v1=d(v0d+iv1d)
(where = real part, = imaginary part). Let's shuffle the first one a bit (using for complex conjugation):
v0=d(v1d+iv0d)=d[12(v1d+v1d)+12(v0dv0d)]= =d2(v0+v1dv0v1d)=12(v0+v1dd(v0v1)).
The result for v1 can be obtained just by switching v0v1. All that does is changing a sign:
v1=12[v0+v1+dd(v0v1)].

And that's it. All the program does is just this calculation, golfed a bit.

Ramillies
источник
very cool!­­­­­
ngn
I don't know much about Perl, but I think you could merge the two conjugate computations into one to save some bytes.
Joel
1
@Joel — Sadly, I'm pretty sure I can't. The first conjugate is acting on ($^a-$^c) (and only inside a lambda that normalizes this number), the second acts on ($b-$d). So they can't really be reconciled. I could make a function that would just call .conj, but that would only add bytes (because I heavily use the $_ variable, which has the nice property that you can call methods on it without specifying it: .conj instead of $_.conj).
Ramillies
@Ramillies Thanks for the explanation.
Joel
How is the δ's magnitude relevant? You're just dividing by δ, switching the real components, and then multiplying by δ again.
Neil
3

Jelly, 16 bytes

_/×ḋ÷²S¥_/ʋ¥N,$+

Try it online!

A dyadic link taking as its left argument a list of the initial positions [[p0x, p0y], [p1x, p1y]] and its right argument the initial velocities [[v0x, v0y], [v1x, v2y]]. Returns a list of the final velocities [[v0x', v0y'], [v1x', v2y']]

Based on the algorithm used by @Neil’s JavaScript answer so be sure to upvote that one too!

Nick Kennedy
источник
3

C (gcc), 140 132 bytes

f(m,n,o,p,q,r,s,t,a)float*a,m,n,o,p,q,r,s,t;{q-=m;r-=n;m=q*q+r*r,n=o*q+p*r-s*q-t*r;q*=n/m;*a++=o-q;n*=r/m;*a++=p-n;*a++=s+q;*a=t+n;}

Try it online!

Basically a port of @Neil's JavaScript answer, but then @ceilingcat shaved off 8 bytes by cleverly reusing m and n to store temporaries.

G. Sliepen
источник
2

Python 2, 97 92 bytes

m,n,o,p,q,r,s,t=input()
q-=m
r-=n
a=o*q+p*r-s*q-t*r
a/=q*q+r*r
print o-a*q,p-a*r,s+a*q,t+a*r

Try it online!

Modified version of Neil's approach.

Erik the Outgolfer
источник