Зіткнення більярдних куль


24

Враховуючи двовимірні положення та швидкості пари більярдних кульок безпосередньо перед ударом, обчисліть їх швидкості після ідеально пружного зіткнення . Кульки вважаються ідеальними сферами (або рівнозначно: кола) з однаковим радіусом, однаковою масою, рівномірною щільністю і без тертя.

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

Зауважте, що радіус більярдного кулі дорівнює половині відстані між 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приймає чотири складні числа як вхідні дані і повертає два складних числа. Версія без вогків показана нижче.

Безумовно

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

Спробуйте в Інтернеті!

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

{v1=v1dvv2=v2+dv

Нехай x12=x1x2 і v12=v1v2 , маємо

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

У програмі, що не має волі, p12 , v1 - v2, dвідповідають x12 , y12 , і dv , відповідно.


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

@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=p1p0

v0/dv1/dd

v0=d(v1d+iv0d),v1=d(v0d+iv1d)
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.


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!


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.




Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.