Zderzenie bilardowych kulek

24

Biorąc pod uwagę dwuwymiarowe położenia i prędkości pary kulek bilardowych tuż przed uderzeniem, oblicz ich prędkości po idealnie elastycznym zderzeniu . Przyjmuje się, że kule są idealnymi kulami (lub równoważnie: okręgami) o tym samym promieniu, tej samej masie, jednolitej gęstości i bez tarcia.

Dane wejściowe składają się z 8 liczb: p0x,p0y,v0x,v0y,p1x,p1y,v1x,v1ygdzie p0x,p0yjest środek pierwszej kuli, v0x,v0yjej prędkość i podobnie p1x,p1y,v1x,v1ydla drugiej kuli. Możesz zaakceptować dane wejściowe w dowolnej kolejności i uporządkowane w dowolny dogodny sposób, np. Jako tablica 2x2x2, a może tablica 2x2 dla pi dwie tablice o długości 2 dla v0i v1. Dobrze jest również przyjmować liczby zespolone (jeśli twój język je obsługuje) zamiast par xy. Nie należy jednak wprowadzać danych w układzie współrzędnych innym niż kartezjański, tzn. Biegunowe jest niedozwolone.

Zauważ, że promień piłki bilardowej wynosi połowę odległości między p0x,p0yi p1x,p1y, więc nie jest podany jako wyraźna część danych wejściowych.

Napisz program lub funkcję, która generuje lub zwraca 4 liczby w dowolnej wygodnej reprezentacji kartezjańskiej: wartości po zderzeniu z v0x,v0y,v1x,v1y.

schemat kolizji

Możliwym algorytmem jest:

  • znajdź normalną linię, która przechodzi przez oba centra

  • znajdź linię styczną , która przechodzi przez punkt środkowy między dwoma środkami i jest prostopadła do linii normalnej

  • zmieniają się w układzie współrzędnych i rozbić v0x,v0yi v1x,v1ydo ich stycznych i normalnych składników v0t,v0niv1t,v1n

  • zamień normalne komponenty v0i v1, zachowując ich styczne komponenty

  • wróć do pierwotnego układu współrzędnych

Testy (wyniki zaokrąglone do 5 miejsc po przecinku):

   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]

Najkrótsze wygrane. Bez luk.


dzięki @Anush za pomoc w naprawie koloru tła diagramu

ngn
źródło

Odpowiedzi:

16

Python 3 , 67 66 bajtów, 53 bajtów

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

Wypróbuj online!

-1 bajt dzięki @ngn

-13 bajtów dzięki @Neil

Ta funkcja fprzyjmuje cztery liczby zespolone jako dane wejściowe i zwraca dwie liczby zespolone. Wersja bez golfa jest pokazana poniżej.

Bez golfa

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

Wypróbuj online!

Formuła obliczeniowa jest pochodną formuły wektorowej 2D na wiki . Ponieważ m1=m2 , wzór można uprościć do

{v1=v1dvv2=v2+dv

Niech x12=x1x2 a v12=v1v2 , mamy

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

W programie ungolfed, p12, v1 - v2, dodpowiadają x12 , y12 , a dv , odpowiednio.

Joel
źródło
1
dobra robota! takie podejście wygląda inaczej niż odpowiedź Perl6 Ramilliesa, która również używa liczb zespolonych. można zapisać bajt, jeśli zastąpi r=p-qsię p-=qi dalsze wykorzystanie pzamiast r, jak w odpowiedzi Neila js
NGN
1
@ngn, wygląda inaczej, ale jest taki sam, jak słusznie zauważa Joel. Napisałem formułę w formie, która była dobra dla gry w golfa w Perlu 6, a Joel prawdopodobnie użył takiego, który byłby lepszy dla Pythona. W każdym razie nie sądziłem, że ktokolwiek wymyśli rozwiązanie niezależnie wykorzystujące liczby zespolone. Dobra robota!
Ramillies,
3
Fajnie, ale jeśli użyjesz algorytmu w pytaniu, zajmie to tylko 53 bajty ...
Neil
1
@Neil Dzięki za podpowiedź. Obliczenia są teraz znacznie uproszczone.
Joel
3
Naprawdę podoba mi się twoje świetne rozwiązanie i szczegółowe wyjaśnienia!
xnor
11

JavaScript (Node.js) , 90 88 bajtów

(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]

Wypróbuj online! Link zawiera pakiet testowy. Objaśnienie: q,rsą ponownie stosowane jako wektor różnicy między środkami i ujest kwadratem jego długości. vjest różnica w produktach kropka o,pi s,tze q,rtak v/ujest współczynnikiem skalowania q,r, który daje kwoty przeniesione z prędkością o,pdo s,t. Edycja: Zapisano 2 bajty dzięki @Arnauld.

Neil
źródło
nie spodziewałem się, że ktoś uprości algorytm tak szybko, dobrze! oto wizualizacja twojego rozwiązania (z ulepszeniem Arnaulda)
ngn
@ngn Błędny link?
Neil
@ Dziennik rurociągów Neila gitlaba mówi, że powinien tam być. Ctrl + F5? strzały kontrolują czerwoną piłkę. zmiana przyspiesza. testowany w firefox i chromie. ostrzeżenie: dźwięk.
ngn
@ngn Ah, pracuję teraz, dzięki! (Wcześniej miałem 404. Używałem też prywatnej karty, więc domyślnie nie miałem dźwięku, chociaż nie uważałem go za natrętny. I jestem bezużyteczny w Asteroidach, inaczej poprosiłbym o „sesję zdjęciową” „klucz ...)
Neil
8

Perl 6 ,75 64 63 61 bajtów

11 bajtów zapisanych przez przełączanie z mapna for, rezygnując z konieczności umieszczania rzeczy w zmiennych pośrednich, mapaby zobaczyć.

Zapisano 1 bajt, zmieniając ($^a-$^c)².&{$_/abs}na ($^a-$^c).&{$_/.conj}.

2 bajty zapisane dzięki @nwellnhof.

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

Wypróbuj online!


Wyjaśnienie

Kiedy w pierwotnym poście powiedziano, że dane wejściowe mogą być liczbami zespolonymi, trudno było się oprzeć ... Więc to bierze 4 liczby zespolone (pozycja 1, prędkość 1, pozycja 2, prędkość 2) i zwraca prędkości jako liczby zespolone.

Program używa tego samego algorytmu, jak opisano w PO. Jednak przy liczbach zespolonych jest to dość proste. Najpierw zauważmy, że liczba zespolona d=p1p0 punktów od pierwszej piłki do drugiej. Jeśli więc podzielimy przez nią wszystkie prędkości, normalny kierunek nagle zbiega się z osią rzeczywistą, a kierunek styczny z osią urojoną. (To psuje wielkości, ale nas to nie obchodzi.)

Teraz musimy zamienić normalne (tj. Rzeczywiste) części prędkości v0/d i v1/d , a następnie pomnożyć to przezd ponownie, aby normalne (i prędkości) wskazywały właściwy kierunek ( i rozproszyć wielkości). Musimy więc obliczyć

v0=d(v1d+iv0d),v1=d(v0d+iv1d)
(gdzie= część rzeczywista,= część urojona). trochę pierwszy (używającdo kompleksowej koniugacji):
v0=d(v1d+iv0d)=d[12(v1d+v1d)+12(v0dv0d)]= =d2(v0+v1dv0v1d)=12(v0+v1dd(v0v1)).
Wynik dlav1można uzyskać po prostu przełączającv0v1. Wszystko, co robi, to zmiana znaku:
v1=12[v0+v1+dd(v0v1)].

I to wszystko. Wszystko, co robi program, to tylko te obliczenia, trochę golfa.

Ramillies
źródło
bardzo fajny!
ngn
Nie wiem wiele o Perlu, ale myślę, że możesz połączyć dwa obliczenia sprzężone w jedno, aby zaoszczędzić trochę bajtów.
Joel
1
@Joel - Niestety, jestem prawie pewien, że nie mogę. Pierwszy koniugat działa na ($^a-$^c)(i tylko wewnątrz lambda, który normalizuje tę liczbę), drugi działa na ($b-$d). Więc tak naprawdę nie da się ich pogodzić. Mógłbym stworzyć funkcję, która po prostu wywoływałaby .conj, ale dodawałaby tylko bajty (ponieważ intensywnie korzystam ze $_zmiennej, która ma fajną właściwość, którą można wywoływać na niej metody bez określania jej: .conjzamiast $_.conj).
Ramillies,
@Ramillies Dzięki za wyjaśnienie.
Joel
Jak ważna jest wielkość δ? Po prostu dzielisz przez δ, zamieniasz prawdziwe komponenty, a następnie ponownie mnożysz przez δ.
Neil
3

Galaretka , 16 bajtów

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

Wypróbuj online!

Diadadicowy link przyjmujący za lewy argument listę pozycji początkowych, [[p0x, p0y], [p1x, p1y]]a prawy argument prędkości początkowych [[v0x, v0y], [v1x, v2y]]. Zwraca listę końcowych prędkości[[v0x', v0y'], [v1x', v2y']]

Opierając się na algorytmie zastosowanym w odpowiedzi JavaScript @ Neila, pamiętaj, aby głosować również na to!

Nick Kennedy
źródło
3

C (gcc) , 140 132 bajtów

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;}

Wypróbuj online!

Zasadniczo port odpowiedzi JavaScript @ Neila, ale potem @ceilingcat zgolił 8 bajtów, sprytnie wykorzystując ponownie mi nprzechowując pliki tymczasowe.

G. Sliepen
źródło
2

Python 2 , 97 92 bajtów

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

Wypróbuj online!

Zmodyfikowana wersja podejścia Neila.

Erik the Outgolfer
źródło
1

C (gcc) , 77 72 bajtów

f(p,v,q,w,a)_Complex*a,p,v,q,w;{p-=q;p*=creal((v-w)/p);*a=v-p;a[1]=w+p;}

Wypróbuj online!

Na podstawie implementacji @Joel w Pythonie

sufitowy
źródło