Quaternionen multiplizieren

13

Schreiben Sie eine benannte Funktion oder ein benanntes Programm, das das Quaternionsprodukt von zwei Quaternionen berechnet. Verwenden Sie so wenig Bytes wie möglich.

Quaternionen

Quaternionen sind eine Erweiterung der reellen Zahlen, die die komplexen Zahlen weiter erweitert. iQuaternionen verwenden nicht nur eine imaginäre Einheit , sondern drei imaginäre Einheiten i,j,k, die die Beziehungen erfüllen.

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

(Es gibt auch Tabellen auf der Wikipedia-Seite .)

Mit anderen Worten, jede imaginäre Einheit ist ein Quadrat zu -1und das Produkt von zwei verschiedenen imaginären Einheiten ist die verbleibende dritte Einheit mit einem in +/-Abhängigkeit davon, ob die zyklische Reihenfolge (i,j,k)eingehalten wird (dh die Rechtsregel ). Es kommt also auf die Reihenfolge der Multiplikation an.

Eine allgemeine Quaternion ist eine lineare Kombination eines Realteils und der drei imaginären Einheiten. Es wird also durch vier reelle Zahlen beschrieben (a,b,c,d).

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

Wir können also mit der Eigenschaft distributive zwei Quaternionen multiplizieren, wobei wir darauf achten, die Einheiten in der richtigen Reihenfolge zu multiplizieren und im Ergebnis ähnliche Begriffe zu gruppieren.

(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

So gesehen kann die Quaternion-Multiplikation als eine Abbildung von einem Paar von 4-Tupeln auf ein einzelnes 4-Tupel betrachtet werden, was Sie implementieren müssen.

Format

Sie sollten entweder ein Programm oder eine benannte Funktion schreiben . Ein Programm sollte Eingaben von STDIN nehmen und das Ergebnis ausdrucken. Eine Funktion sollte Funktionseingaben aufnehmen und eine Ausgabe zurückgeben (nicht drucken).

Eingabe- und Ausgabeformate sind flexibel. Die Eingabe besteht aus acht reellen Zahlen (die Koeffizienten für zwei Quaternionen), und die Ausgabe besteht aus vier reellen Zahlen. Die Eingabe kann aus acht Zahlen, zwei Listen mit vier Zahlen, einer 2x4-Matrix usw. bestehen. Das Eingabe- / Ausgabeformat muss nicht identisch sein. Die Reihenfolge der (1,i,j,k)Koeffizienten liegt bei Ihnen.

Die Koeffizienten können negativ oder nicht ganz sein. Sorgen Sie sich nicht um echte Präzision oder Überläufe.

Verboten: Funktion oder Typen speziell für Quaternionen oder Äquivalente.

Testfälle

Diese sind im (1,i,j,k)Koeffizientenformat.

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

Referenzimplementierung

In Python als Funktion:

#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
xnor
quelle

Antworten:

4

CJam, 49 45 39 Bytes

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

Das Obige verwendet Caret- und M-Notation, da der Code nicht druckbare Zeichen enthält.

Auf Kosten von zwei zusätzlichen Bytes können diese Zeichen vermieden werden:

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

Sie können diese Version online testen : CJam-Interpreter

Testfälle

(a + bi + cj + dk) * (e + fi + gj + hk)Verwenden Sie zur Berechnung die folgende Eingabe:

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

Die Ausgabe wird sein

[ z y x w ]

das entspricht der quaternion 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]

Wie es funktioniert

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.              ";
Dennis
quelle
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]

Ordnet zwei Listen A,Ban [1,i,j,k]und gibt ein Ergebnis im gleichen Format zurück.

Die Schlüsselidee ist, dass Sie bei der [1,i,j,k]Entsprechung zu Indizes [0,1,2,3]den Index des Produkts (bis zum Vorzeichen) durch XOR'-Verknüpfung der Indizes erhalten. Die Begriffe, die in den Index aufgenommen pwerden p, sind also diejenigen, für die XOR indiziert wird , und somit die Produkte A[m]*B[m^p].

Es bleibt nur noch zu machen, die Zeichen funktionieren. Der kürzeste Weg, den ich gefunden habe, bestand darin, sie einfach in eine magische Zeichenfolge zu codieren. Die 16 Möglichkeiten für (m,p)werden in Zahlen 0zu 15as umgewandelt p+4*m. Die Zahl 14672in Binärschrift steht 1an den Stellen, an denen -1Zeichen benötigt werden. Durch Verschieben der entsprechenden Anzahl von Stellen wird ein 1oder 0bei der letzten Ziffer angezeigt, wodurch die Zahl ungerade oder gerade wird, und dies (-1)**ist entweder 1oder -1nach Bedarf.

xnor
quelle
Der XOR-Teil ist pure Genialität.
Dennis
3

Python - 90 75 72 69

Pure Python, keine Bibliotheken - 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]

Es ist wahrscheinlich ziemlich schwierig, diese "Standard" -Lösung in Python zu verkürzen. Ich bin aber sehr gespannt, was sich die anderen einfallen lassen. :)


Verwenden von NumPy - 75 72 69:

Nun, da die Eingangs- und Ausgangs ziemlich flexibel sind, können wir einige NumPy Funktionen nutzen und nutzen die Skalar-Vektordarstellung :

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

Eingabeargumente sund tsind die skalaren Teile der beiden Quaternionen (die Realteile) und pund qsind die entsprechenden Vektorteile (die imaginären Einheiten). Die Ausgabe ist eine Liste mit Skalar- und Vektorteilen des resultierenden Quaternions, wobei letzteres als NumPy-Array dargestellt wird.

Einfaches Testskript:

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(...)Ist die Referenzimplementierung des OP.)

Ausgabe:

[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])]
Falko
quelle
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]

Das Portieren nach Haskell erspart uns ein paar Zeichen;)

ThreeFx
quelle
2

Mathematica 83 50

Wahrscheinlich kann mehr golfen werden ..

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

Leerzeichen und Zeilenumbrüche werden nicht gezählt und nicht benötigt.

Verwendung:

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


BEARBEITEN Wie das funktioniert.

Die Mathematica-Funktion Permutationsmacht alle möglichen Permutationen von #2(dem zweiten Argument). Es gibt 24 Permutationen, aber wir brauchen nur {e,f,g,h}, {f,e,h,g}, {g,h,e,f}, und {h,g,f,e}. Dies sind die erste, 8., 17. und 24. Permutation. Also der Code

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

Wählt diese genau aus den Permutationen des zweiten Arguments aus und gibt sie als Matrix zurück. Dann haben sie aber noch nicht das richtige Vorzeichen. Der Code p[{-1,1,-1,1}][[1;;3]]gibt eine 3x4-Matrix mit dem richtigen Vorzeichen zurück. Wir stellen es voran, {1,1,1,1}indem Joinwir eine normale Multiplikation ( Timesoder wie hier, indem wir sie einfach nacheinander schreiben) zwischen zwei Matrizen verwenden und in Mathematica eine elementweise Multiplikation durchführen.

Also endlich das Ergebnis von

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

ist die Matrix

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

Eine Matrixmultiplikation zwischen {a,b,c,d}(dem ersten Argument #1) und der vorherigen Matrix ergibt das gewünschte Ergebnis.



EDIT 2 Kürzere Code

Inspiriert durch den Python-Code von Falko habe ich das Quaternion in einen Skalar- und einen Vektorteil aufgeteilt und den in Mathematica eingebauten Befehl verwendet Cross, um das Kreuzprodukt der Vektorteile zu berechnen:

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

Verwendung:

f[a,{b,c,d},e,{f,g,h}]        (* => {x,w,y,z}   *)
Freddieknets
quelle
Könnten Sie erklären, wie das funktioniert? Was ist 1, 8, 17, 24?
Xnor
1

Python, 94

Der einfachste Weg ist nicht zu lang.

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]
Florian F
quelle
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]
William Barbosa
quelle
1

Lua - 99

Könnte auch.

_,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)

Luas "unpack ()" befreit die Elemente einer Tabelle. In der Tabelle 'arg' werden also alle Kommandozeilen-Eingaben gespeichert (einschließlich arg[0]des Dateinamens des Programms, der verworfen wird).

AndoDaan
quelle
1

Python, 58 56 Zeichen

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

Ich nehme sehr großzügigen Einsatz der Ein- / Ausgabeformat Wiggleraum. Die Eingaben sind 4 komplexe Zahlen, die folgendermaßen codiert sind:

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

Es gibt ein Paar komplexer Zahlen in einem ähnlichen Format aus. Das erste Paar codiert den Realteil und den iTeil, das zweite codiert den Teil jund den kTeil.

Beachten Sie, dass das erste Quaternion x+y*jund das zweite Quaternion ist, um zu sehen, dass das funktioniert z+w*j. Bewerten (x+y*j)*(z+w*j)und realisieren Sie das j*t= conj(t)*jfür jede imaginäre Zahl t.

Keith Randall
quelle
Sehr schlau! Wissen Sie, warum sich die Quaternionen wie komplexe Zahlen mit komplexen Koeffizienten zu multiplizieren scheinen, wie es aus Ihrem Ausdruck hervorgeht?
xnor
Egal, ich verstehe jetzt aus Ihrer Erklärung, wie iund jwie sich innere und äußere komplexe Koeffizienten verhalten. Wie faszinierend!
xnor
Es ist lustig, wie die Konjugationsanrufe mehr als 2/5 Ihrer Zeichen einnehmen. Ich denke, Sie können einen Saibling mit rasieren (2*w.real-w). abs(w)**2/wwürde aber für 0 funktionieren. Vielleicht wäre sogar exec mit String-Ersetzung es wert? `
xnor
1

Whispers v2 , 396 Bytes

> 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

Probieren Sie es online!

Nimmt Eingaben in das Formular auf

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

und Ausgänge als

w
x
y
z

zu repräsentieren q=w+xich+yj+zk

Der Strukturbaum dieser Antwort lautet:

tree

Ein guter Teil dieser Antwort rührt von zwei Hauptfehlern in Whispers her:

  • Keine Funktion zum Umkehren eines Arrays
  • Die Verwendung von Mengen bei der Berechnung des kartesischen Produkts

Daher können wir den Code in drei Abschnitte aufteilen.

Wie es funktioniert

Die folgenden Definitionen dienen der Klarheit und Übersichtlichkeit:

q=ein+bich+cj+dk
p=e+fich+Gj+hk
r=w+xich+yj+zk,(qp=r)
EIN=[ein,b,c,d]
B=[e,f,G,h]
C=[w,x,y,z]

Abschnitt 1: Permutieren EIN und B

Der erste Abschnitt ist mit Abstand der längste und erstreckt sich von Linie 1 bis Linie 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

Der Hauptzweck dieses Abschnitts ist das Permutieren B so dass einfache elementweise Multiplikation zwischen EIN und Bist möglich. Es gibt vier verschiedene Anordnungen vonB die Elemente von multiplizieren EIN mit:

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

Die zweite Eingabe, Bwird in Zeile 6 gespeichert . Wir haben uns dann getrenntB in der Mitte, wie jede mögliche Anordnung von Bist paarweise gruppiert. Um diese Paare umzukehren (um die richtigen Befehle zu erhalten)B2 und B4) nehmen wir das erste und das letzte Element und verketten sie dann in umgekehrter Reihenfolge:

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

(Bildung [f,e]) und

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

(Bildung [h,G]). Wir haben jetzt alle Hälften, die zur Bildung der Arrangements benötigt werden, und verknüpfen sie zu einer EinheitB1,B2,B3 und B4. Schließlich verknüpfen wir diese vier Arrangements zu einer EinheitBT. Wir machen es dann schnellEINT, definiert als EIN wiederholt 4 mal:

EINT=[ein,b,c,d,ein,b,c,d,ein,b,c,d,ein,b,c,d]
BT=[e,f,G,h,f,e,h,G,G,h,e,f,h,G,f,e]

Wenn jedes Element von BT wird mit dem entsprechenden Element in multipliziert EINTerhalten wir die (vorzeichenlosen) Werte in qp

Abschnitt 2: Zeichen und Produkte

Wie in Abschnitt 1 erwähnt, sind die Werte in EINT und BT entsprechen den vorzeichenlosen (dh positiven) Werten jedes der Koeffizienten in qp. Die Zeichen enthalten kein offensichtliches Muster, das kürzer wäre, als das Array nur hart zu codieren. Daher codieren wir das Array hart:

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

Wir werden dieses Array nennen S(Zeichen). Als nächstes zippen wir jedes Element zusammenEINT,BT und S und nimm das Produkt jedes Subarrays, z [[a,e,1],[b,f,1],,[e,f,1],[d,e,1]]D=[ae,bf,,ef,de].

Section 3: Partitions and final sums.

Once we have the array of coefficients of qp, with signs, we need to split it into 4 parts (i.e. the four factorised coefficients of qp), and then take the sums. This leads us to the only golfing opportunity found: moving the

> 4

to line 4 rather than 26, as it is used 6 times, each time saving a byte by moving it. Unfortunately, this costs a byte changing the 9 to a 10, so only 5 bytes are saved. The next section takes slices of size 4 from the front of D, saving each slice to the corresponding row and passing on the shortened list of D. Once 4 slices are taken, we the take the sum of each, before outputting them all.

caird coinheringaahing
quelle