Collisione di palle da biliardo


24

Date le posizioni e le velocità bidimensionali di una coppia di palle da biliardo subito prima dell'impatto, calcola le loro velocità dopo una collisione perfettamente elastica . Si presume che le sfere siano sfere ideali (o equivalentemente: cerchi) con lo stesso raggio, stessa massa, densità uniforme e nessun attrito.

L'ingresso è composto da 8 numeri: p0x,p0y,v0x,v0y,p1x,p1y,v1x,v1ydov'è p0x,p0yil centro della prima palla, la v0x,v0ysua velocità e similmente p1x,p1y,v1x,v1yper la seconda palla. È possibile accettare input in qualsiasi ordine e strutturati in qualsiasi modo conveniente, ad esempio come un array 2x2x2 o forse un array 2x2 per pe due array di lunghezza 2 per v0e v1. Va anche bene prendere numeri complessi (se la tua lingua li supporta) invece di coppie xy. Tuttavia, non si dovrebbe prendere input in un sistema di coordinate diverso da cartesiano, cioè non è consentito il polar.

Nota che il raggio di una palla da biliardo è metà della distanza tra p0x,p0ye p1x,p1y, quindi non è dato come parte esplicita dell'input.

Scrivi un programma o una funzione che emetta o restituisca 4 numeri in qualsiasi rappresentazione cartesiana conveniente: i valori post-collisione di v0x,v0y,v1x,v1y.

collision diagram

Un possibile algoritmo è:

  • trova la linea normale che attraversa entrambi i centri

  • trova la linea tangente che passa attraverso il punto medio tra i due centri ed è perpendicolare alla linea normale

  • cambiare il sistema di coordinate e scomporre v0x,v0ye v1x,v1ynei loro componenti tangenziali e normali v0t,v0nev1t,v1n

  • scambiare i componenti normali di v0e v1, preservandone i componenti tangenziali

  • tornare al sistema di coordinate originale

Test (risultati arrotondati al 5 ° decimale):

   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]

Vittorie più brevi. Nessuna scappatoia.


grazie @Anush per l' aiuto nella correzione del colore di sfondo del diagramma

Risposte:


16

Python 3 , 67 66 byte, 53 byte

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

Provalo online!

-1 byte grazie a @ngn

-13 byte grazie a @Neil

Questa funzione faccetta quattro numeri complessi come input e restituisce due numeri complessi. La versione non golfata è mostrata di seguito.

Ungolfed

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

Provalo online!

La formula di calcolo è derivata in base alla formula vettoriale 2D su wiki . Poiché m1=m2 , la formula può essere semplificata in

{v1=v1dvv2=v2+dv

Sia x12=x1x2 ev12=v1v2 , abbiamo

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

Nel programma ungolfed, p12, v1 - v2, dcorrispondono a x12 ,y12 , edv, rispettivamente.


1
molto bene! questo approccio sembra diverso dalla risposta perl6 di Ramillies che utilizza anche numeri complessi. potresti salvare un byte se lo sostituisci r=p-qcon p-=qe utilizzi ulteriormente pinvece di r, come nella risposta js di Neil
ngn

1
@ngn, sembra diverso ma è lo stesso, come osserva correttamente Joel. Ho scritto la formula in una forma che andava bene per il golf Perl 6 e presumibilmente Joel ne usava una migliore per Python. Comunque, non pensavo che nessun altro avrebbe trovato una soluzione usando numeri complessi in modo indipendente. Buon lavoro!
Ramillies,

3
Bello, ma se avessi usato l'algoritmo nella domanda, ci sarebbero voluti solo 53 byte ...
Neil,

1
@Neil Grazie per il tuo suggerimento. Il calcolo ora è notevolmente semplificato.
Gioele il

3
Mi piacciono davvero tutte le tue fantastiche soluzioni e spiegazioni dettagliate!
xnor

11

JavaScript (Node.js) , 90 88 byte

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

Provalo online! Il link include la suite di test. Spiegazione: q,rvengono riproposti come vettore di differenza tra i centri ed uè il quadrato della sua lunghezza. vè la differenza tra i prodotti a punti di o,pe s,tcon q,r, quindi anche v/uil fattore di ridimensionamento q,rindica la quantità di velocità trasferita da o,pa s,t. Modifica: salvato 2 byte grazie a @Arnauld.


non mi aspettavo che qualcuno avrebbe semplificato l'algoritmo così in fretta, ben fatto! ecco una visualizzazione della tua soluzione (con il miglioramento di Arnauld)
ngn

@ngn Link errato?
Neil

Il registro delle pipeline di @Neil gitlab dice che dovrebbe essere lì. CTRL + F5? le frecce controllano la palla rossa. il cambio accelera. testato su firefox e cromo. avvertimento: suono.
ngn

@ngn Ah, adesso lavora, grazie! (Avevo già un 404. Inoltre, stavo usando una scheda privata, quindi non avevo l'audio di default, anche se non lo trovavo invadente. E sono inutile agli Asteroidi, altrimenti chiederei un "tiro "key ...)
Neil

8

Perl 6 ,75 64 63 61 byte

11 byte salvati passando da mapa for, eliminando la necessità di mettere le cose in variabili intermedie per il mapvedere.

1 byte salvato cambiando ($^a-$^c)².&{$_/abs}in ($^a-$^c).&{$_/.conj}.

2 byte salvati grazie a @nwellnhof.

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

Provalo online!


Spiegazione

Quando il post originale diceva che l'ingresso poteva essere numeri complessi, era troppo difficile resistere ... Quindi questo richiede 4 numeri complessi (posizione 1, velocità 1, posizione 2, velocità 2) e restituisce le velocità come numeri complessi.

Il programma utilizza esattamente lo stesso algoritmo descritto nell'OP. Tuttavia, con numeri complessi, è abbastanza semplice. Innanzitutto, notiamo che il numero complessod=p1-p0punti dalla prima palla alla seconda. Quindi, se dividiamo tutte le velocità per essa, la direzione normale coincide improvvisamente con l'asse reale e la direzione tangente con l'asse immaginario. (Questo incasina le magnitudini ma non ci interessa.)

Ora, abbiamo bisogno di cambiare le parti normali (cioè reali) delle velocità v0/d e v1/de successivamente, moltiplicalo per 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.


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.




Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.