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,(q⋅p=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 q⋅p

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 q⋅p. 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 q⋅p, con segni, dobbiamo dividerlo in 4 parti (ovvero i quattro coefficienti fattorizzati di q⋅p), 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.