Moltiplica i quaternioni


13

Scrivi una funzione o un programma denominato che calcola il prodotto quaternione di due quaternioni. Utilizzare il minor numero di byte possibile.

quaternions

I quaternioni sono un'estensione dei numeri reali che estende ulteriormente i numeri complessi. Piuttosto che una singola unità immaginaria i, i quaternioni usano tre unità immaginarie i,j,kche soddisfano le relazioni.

i*i = j*j = k*k = -1
i*j =  k
j*i = -k
j*k =  i
k*j = -i
k*i =  j
i*k = -j

(Ci sono anche tabelle di questi sulla pagina di Wikipedia .)

In parole, ogni unità immaginaria viene quadrata -1e il prodotto di due diverse unità immaginarie è il terzo rimanente con una a +/-seconda che l'ordine ciclico (i,j,k)sia rispettato (cioè la regola della mano destra ). Quindi, l'ordine della moltiplicazione conta.

Un quaternione generale è una combinazione lineare di una parte reale e le tre unità immaginarie. Quindi, è descritto da quattro numeri reali (a,b,c,d).

x = a + b*i + c*j + d*k

Quindi, possiamo moltiplicare due quaternioni usando la proprietà distributiva, facendo attenzione a moltiplicare le unità nell'ordine giusto e raggruppando termini simili nel risultato.

(a + b*i + c*j + d*k) * (e + f*i + g*j + h*k)
= (a*e - b*f - c*g - d*h)    +
  (a*f + b*e + c*h - d*g)*i  +
  (a*g - b*h + c*e + d*f)*j  +
  (a*h + b*g - c*f + d*e)*k

Vista in questo modo, la moltiplicazione del quaternione può essere vista come una mappa da una coppia di 4 tuple a una singola 4 tuple, che è ciò che ti viene chiesto di implementare.

Formato

È necessario scrivere un programma o una funzione denominata . Un programma dovrebbe prendere input da STDIN e stampare il risultato. Una funzione dovrebbe accettare input di funzione e restituire (non stampare) un output.

I formati di input e output sono flessibili. L'input è composto da otto numeri reali (i coefficienti per due quaternioni) e l'output è costituito da quattro numeri reali. L'ingresso può essere composto da otto numeri, due elenchi di quattro numeri, una matrice 2x4, ecc. Il formato di input / output non deve essere lo stesso. L'ordinamento dei (1,i,j,k)coefficienti dipende da te.

I coefficienti possono essere negativi o non interi. Non preoccuparti della vera precisione o traboccamenti.

Vietato: funzione o tipi specifici per quaternioni o equivalenti.

Casi test

Questi sono in (1,i,j,k)formato coefficiente.

[[12, 54, -2, 23], [1, 4, 6, -2]] 
 [-146, -32, 270, 331]

[[1, 4, 6, -2], [12, 54, -2, 23]] 
 [-146, 236, -130, -333]

[[3.5, 4.6, -0.24, 0], [2.1, -3, -4.3, -12]] 
 [20.118, 2.04, 39.646, -62.5]

Implementazione di riferimento

In Python, come funzione:

#Input quaternions: [a,b,c,d], [e,f,g,h]
#Coeff order: [1,i,j,k]

def mult(a,b,c,d,e,f,g,h):
    coeff_1 = a*e-b*f-c*g-d*h
    coeff_i = a*f+b*e+c*h-d*g
    coeff_j = a*g-b*h+c*e+d*f
    coeff_k = a*h+b*g-c*f+d*e

    result = [coeff_1, coeff_i, coeff_j, coeff_k]
    return result

Risposte:


4

CJam, 49 45 39 byte

"cM-^\M-^G-^^KM-zP"256bGbq~m*f{=:*}4/{:-W*}/W*]`

Quanto sopra utilizza il punto di inserimento e la notazione M, poiché il codice contiene caratteri non stampabili.

Al costo di due byte aggiuntivi, questi caratteri possono essere evitati:

6Z9C8 7YDXE4BFA5U]q~m*f{=:*}4/{:-W*}/W*]`

Puoi provare questa versione online: interprete CJam

Casi test

Per calcolare (a + bi + cj + dk) * (e + fi + gj + hk), utilizzare il seguente input:

[ d c b a ] [ h g f e ]

L'output sarà

[ z y x w ]

che corrisponde al quaternione w + xi + yj + zk.

$ base64 -d > product.cjam <<< ImOchy0eS/pQIjI1NmJHYnF+bSpmez06Kn00L3s6LVcqfS9XKl1g
$ wc -c product.cjam
39 product.cjam
$ LANG=en_US cjam product.cjam <<< "[23 -2 54 12] [-2 6 4 1]"; echo
[331 270 -32 -146]
$ LANG=en_US cjam product.cjam <<< "[-2 6 4 1] [23 -2 54 12]"; echo
[-333 -130 236 -146]
$ LANG=en_US cjam product.cjam <<< "[0 -0.24 4.6 3.5] [-12 -4.3 -3 2.1]"; echo
[-62.5 39.646 2.04 20.118]

Come funziona

6Z9C8 7YDXE4BFA5U]  " Push the array [ 6 3 9 12 8 7 2 13 1 14 4 11 15 10 5 0].         ";
q~                  " Read from STDIN and interpret the input.                         ";
m*                  " Compute the cartesian product of the input arrays.               ";
f                   " Execute the following for each element of the first array:       ";
{                   " Push the cartesian product (implicit).                           ";
    =               " Retrieve the corresponding pair of coefficients.                 ";
    :*              " Calculate their product.                                         ";
}                   "                                                                  ";
4/                  " Split into chunks of 4 elements.                                 ";
{:-W*}/             " For each, subtract the first element from the sum of the others. ";
W*                  " Multiply the last integers (coefficient of 1) by -1.             ";
]`                  " Collect the results into an array and stringify it.              ";

6

Python (83)

r=lambda A,B,R=range(4):[sum(A[m]*B[m^p]*(-1)**(14672>>p+4*m)for m in R)for p in R]

Accetta due elenchi A,Bin [1,i,j,k]ordine e restituisce un risultato nello stesso formato.

L'idea chiave è che con gli [1,i,j,k]indici corrispondenti [0,1,2,3], si ottiene l'indice del prodotto (fino al segno) XOR'ing gli indici. Quindi, i termini che vengono inseriti nell'indice psono quelli a cui indicizza XORp e quindi i prodotti A[m]*B[m^p].

Resta solo da far funzionare i segni. Il modo più breve che ho trovato è stato semplicemente codificarli in una stringa magica. Le 16 possibilità per (m,p)vengono trasformate in numeri 0in 15as p+4*m. Il numero 14672in binario è 1nei punti in cui -1sono necessari i segni. Spostando il numero appropriato di posti, a 1o si 0chiude all'ultima cifra, rendendo il numero dispari o pari, e così (-1)**è o 1o -1secondo necessità.


La parte XOR è pura genialità.
Dennis,

3

Python - 90 75 72 69

Pure Python, no librerie - 90:

m=lambda a,b,c,d,e,f,g,h:[a*e-b*f-c*g-d*h,a*f+b*e+c*h-d*g,a*g-b*h+c*e+d*f,a*h+b*g-c*f+d*e]

Probabilmente è abbastanza difficile abbreviare questa soluzione "predefinita" in Python. Ma sono molto curioso di sapere cosa potrebbe pensare l'altro. :)


Utilizzando NumPy - 75 72 69:

Bene, poiché l'input e l'output sono piuttosto flessibili, possiamo usare alcune funzioni NumPy e sfruttare la rappresentazione vettoriale scalare :

import numpy
m=lambda s,p,t,q:[s*t-sum(p*q),s*q+t*p+numpy.cross(p,q)]

Input argomenti se tsono le parti scalari dei due quaternioni (le parti reali) e peq sono le corrispondenti parti vettoriali (le unità immaginarie). L'output è un elenco contenente parte scalare e parte vettoriale del quaternione risultante, quest'ultimo rappresentato come matrice NumPy.

Script di test semplice:

for i in range(5):
    a,b,c,d,e,f,g,h=np.random.randn(8)
    s,p,t,q=a, np.array([b, c, d]), e, np.array([f, g, h])
    print mult(a, b, c, d, e, f, g, h), "\n", m(s,p,t,q)

(mult(...) essendo l'implementazione di riferimento del PO).

Produzione:

[1.1564241702553644, 0.51859264077125156, 2.5839001110572792, 1.2010364098925583] 
[1.1564241702553644, array([ 0.51859264,  2.58390011,  1.20103641])]
[-1.8892934508324888, 1.5690229769129256, 3.5520713781125863, 1.455726589916204] 
[-1.889293450832489, array([ 1.56902298,  3.55207138,  1.45572659])]
[-0.72875976923685226, -0.69631848934167684, 0.77897519489219036, 1.4024428845608419] 
[-0.72875976923685226, array([-0.69631849,  0.77897519,  1.40244288])]
[-0.83690812141836401, -6.5476014589535243, 0.29693969165495304, 1.7810682337361325] 
[-0.8369081214183639, array([-6.54760146,  0.29693969,  1.78106823])]
[-1.1284033842268242, 1.4038096725834259, -0.12599103441714574, -0.5233468317643214] 
[-1.1284033842268244, array([ 1.40380967, -0.12599103, -0.52334683])]

2

Haskell, 85

m a b c d e f g h=[a*e-b*f-c*g-d*h,a*f+b*e+c*h-d*g,a*g-b*h+c*e+d*f,a*h+b*g-c*f+d*e]

Portarlo su Haskell ci fa risparmiare qualche carattere;)


2

matematica 83 50

Probabilmente si può giocare a golf di più ..

p = Permutations;
f = #1.(Join[{{1, 1, 1, 1}}, p[{-1, 1, -1, 1}][[1 ;; 3]]] p[#2][[{1, 8, 17, 24}]]) &

Spazi e newline non conteggiati e non necessari.

Uso:

f[{a,b,c,d},{e,f,g,h}]        (* => {x,w,y,z}   *)


MODIFICARE Come funziona.

La funzione Mathematica Permutationsrende possibili tutte le permutazioni di #2(il secondo argomento). Ci sono 24 permutazioni, ma abbiamo solo bisogno {e,f,g,h}, {f,e,h,g}, {g,h,e,f}, e {h,g,f,e}. Queste sono le permutazioni prima, ottava, diciassettesima e ventiquattresima. Quindi il codice

p[#2][[{1,8,17,24}]]

li seleziona esattamente tra le permutazioni del secondo argomento e li restituisce come una matrice. Ma poi non hanno ancora il segno giusto. Il codice p[{-1,1,-1,1}][[1;;3]]restituisce una matrice 3x4 con il segno corretto. Lo anteponiamo {1,1,1,1}utilizzando Joine facendo una normale moltiplicazione (Times o come nel caso qui semplicemente scrivendoli uno dopo l'altro) tra due matrici si ottiene una moltiplicazione elemento per elemento in Mathematica.

Quindi, finalmente, il risultato di

(Join[{{1, 1, 1, 1}}, p[{-1, 1, -1, 1}][[1 ;; 3]]] p[#2][[{1, 8, 17, 24}]])

è la matrice

 e  f  g  h
-f  e -h  g
-g  h  e -f
-h -g  f  e

Effettuare una moltiplicazione di matrice tra {a,b,c,d}(il primo argomento #1) e la matrice precedente dà il risultato desiderato.



MODIFICA 2 Codice più breve

Ispirato al codice Python di Falko, ho diviso il quaternione in una parte scalare e una parte vettoriale e utilizzo il comando integrato di Mathematica Crossper calcolare il prodotto incrociato delle parti vettoriali:

f[a_, A_, b_, B_] := Join[{a*b - A.B}, a*B + b*A + Cross[A, B]]

Uso:

f[a,{b,c,d},e,{f,g,h}]        (* => {x,w,y,z}   *)

Potresti spiegare come funziona? Cosa sono 1, 8, 17, 24?
xnor

1

Python, 94

Il modo più semplice non è troppo lungo.

def m(a,b,c,d,e,f,g,h):return[a*e-b*f-c*g-d*h,a*f+b*e+c*h-d*g,a*g-b*h+c*e+d*f,a*h+b*g-c*f+d*e]

1

JavaScript ES6 - 86

f=(a,b,c,d,e,f,g,h)=>[a*e-b*f-c*g-d*h,a*f+b*e+c*h-d*g,a*g-b*h+c*e+d*f,a*h+b*g-c*f+d*e]

1

Lua - 99

Potrebbe pure.

_,a,b,c,d,e,f,g,h=unpack(arg)print(a*e-b*f-c*g-d*h,a*f+b*e+c*h-d*g,a*g-b*h+c*e+d*f,a*h+b*g-c*f+d*e)

"Unpack ()" di Lua libera gli elementi di una tabella. Quindi la tabella 'arg' è dove è archiviato tutto l'input della riga di comando (incluso arg[0]quale è il nome del file del programma, viene scartato).


1

Python, 58 56 caratteri

m=lambda x,y,z,w:(x*z-y*(2*w.real-w),x*w+y*(2*z.real-z))

Prendo un uso molto liberale del wiggle room del formato input / output. Gli ingressi sono 4 numeri complessi, codificati così:

x = a+b*i
y = c+d*i
z = e+f*i
w = g+h*i

Produce una coppia di numeri complessi in un formato simile, il primo della coppia codifica il reale e la iparte, il secondo codifica la je kparti.

Per vedere che funziona, nota che il primo quaternione è x+y*je il secondo è z+w*j. Basta valutare (x+y*j)*(z+w*j)e rendersi conto che j*t= conj(t)*jper qualsiasi numero immaginario t.


Molto intelligente! Sai perché i quaternioni sembrano moltiplicarsi come numeri complessi con coefficienti complessi, come appare dalla tua espressione?
xnor

Non importa, ora capisco dalla tua spiegazione come ie jagisco come coefficienti complessi interni ed esterni. Che affascinante!
xnor

È divertente come le chiamate di congiunzione occupino più di 2/5 dei tuoi caratteri. Penso che puoi radere un carattere ciascuno usando (2*w.real-w). abs(w)**2/wavrebbe funzionato ma per 0. Forse vale la pena anche exec con sostituzione di stringa? `
xnor

1

Whispers v2 , 396 byte

> 1
> 2
> 0
> 4
> Input
> Input
>> 6ᶠ2
>> 6ᵗ2
>> 7ⁿ3
>> 7ⁿ1
>> 10‖9
>> 8ⁿ3
>> 8ⁿ1
>> 13‖12
>> 7‖8
>> 11‖14
>> 8‖7
>> 14‖11
>> 15‖16
>> 19‖17
>> 20‖18
>> 4⋅5
>> L⋅R
>> Each 23 22 21
> [1,-1,-1,-1,1,1,1,-1,1,-1,1,1,1,1,-1,1]
>> Each 23 24 25
>> 26ᶠ4
>> 26ᵗ4
>> 28ᶠ4
> 8
>> 26ᵗ30
>> 31ᶠ4
>> 31ᵗ4
>> ∑27
>> ∑29
>> ∑32
>> ∑33
>> Output 34 35 36 37

Provalo online!

Accetta input nel modulo

[a, b, c, d]
[e, f, g, h]

e produce come

w
x
y
z

q=w+Xio+yj+zK

L'albero della struttura di questa risposta è:

albero

Una buona parte di questa risposta proviene da due difetti principali di Whispers:

  • Nessuna funzione per invertire un array
  • L'uso di set nel calcolo del prodotto cartesiano

Pertanto, possiamo dividere il codice in 3 sezioni.

Come funziona

Useremo le seguenti definizioni per chiarezza e concisione:

q=un'+Bio+cj+dK
p=e+fio+gj+hK
r=w+Xio+yj+zK,(qp=r)
UN=[un',B,c,d]
B=[e,f,g,h]
C=[w,X,y,z]

Sezione 1: Permuting UN e B

La prima sezione è di gran lunga la più lunga, che si estende dalla linea 1 alla linea 22 :

> 1
> 2
> 0
> 4
> Input
> Input
>> 6ᶠ2
>> 6ᵗ2
>> 7ⁿ3
>> 7ⁿ1
>> 10‖9
>> 8ⁿ3
>> 8ⁿ1
>> 13‖12
>> 7‖8
>> 11‖14
>> 8‖7
>> 14‖11
>> 15‖16
>> 19‖17
>> 20‖18
>> 4⋅5

Lo scopo principale di questa sezione è permutare B in modo tale semplice moltiplicazione tra gli elementi UN e Bè possibile. Ci sono quattro diversi arrangiamenti diB per moltiplicare gli elementi di UN con:

B1=[e,f,g,h]
B2=[f,e,h,g]
B3=[g,h,e,f]
B4=[h,g,f,e]

Il secondo input, B, è memorizzato sulla linea 6 . Ci siamo quindi separatiB nel mezzo, come ogni possibile disposizione di Bè raggruppato in coppie. Per invertire queste coppie (per ottenere gli ordini corretti inB2 e B4), prendiamo il primo e l'ultimo elemento, quindi li concateniamo in ordine inverso:

>> 7ⁿ3
>> 7ⁿ1
>> 10‖9

(formando [f,e]) e

>> 8ⁿ3
>> 8ⁿ1
>> 13‖12

(formando [h,g]). Ora abbiamo tutte le metà necessarie per formare gli accordi, quindi li concateniamo insieme per formareB1,B2,B3 e B4. Infine, concateniamo questi quattro accordi insieme per formareBT. Quindi facciamo rapidamenteUNT, definito come UN ripetuto 4 volte:

UNT=[un',B,c,d,un',B,c,d,un',B,c,d,un',B,c,d]
BT=[e,f,g,h,f,e,h,g,g,h,e,f,h,g,f,e]

Quando ogni elemento di BT viene moltiplicato per l'elemento corrispondente in UNT, otteniamo i valori (senza segno) in qp

Sezione 2: Segni e prodotti

Come detto nella Sezione 1, i valori in UNT e BT corrisponde ai valori senza segno (cioè positivi) di ciascuno dei coefficienti in qp. Non è stato trovato alcun modello evidente nei segni che sarebbe più breve della semplice codifica dell'array, quindi codifichiamo l'array:

> [1,-1,-1,-1,1,1,1,-1,1,-1,1,1,1,1,-1,1]

Chiameremo questo array S(segni). Quindi, comprimiamo insieme ogni elementoUNT,BT e S e prendere il prodotto di ciascun sotto-array, ad es [[un',e,1],[B,f,-1],...,[e,f,-1],[d,e,1]]D=[un'e,-Bf,...,-ef,de].

Sezione 3: Partizioni e somme finali.

Una volta che abbiamo l'array di coefficienti di qp, con segni, dobbiamo dividerlo in 4 parti (ovvero i quattro coefficienti fattorizzati di qp), quindi prendere le somme. Questo ci porta all'unica opportunità di golf trovata: spostare il

> 4

alla riga 4 anziché 26 , poiché viene utilizzato 6 volte, salvando ogni volta un byte spostandolo. Sfortunatamente, questo costa un byte che cambia il 9 in un 10 , quindi solo5i byte vengono salvati. La sezione successiva prende sezioni di dimensioni4 dalla parte anteriore di D, salvando ciascuna sezione nella riga corrispondente e passando l'elenco abbreviato di D. Una volta prese 4 sezioni, prendiamo la somma di ciascuna, prima di emetterle tutte.

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.