Mnożenie czwartorzędów

13

Napisz nazwaną funkcję lub program, który oblicza iloczyn czwartorzędu z dwóch czwartorzędów. Użyj jak najmniej bajtów.

Czwartorzędy

Czwartorzędy są rozszerzeniem liczb rzeczywistych, który dodatkowo rozszerza liczby zespolone. Zamiast jednej urojonej jednostki i, czwartorzędy używają trzech urojonych jednostek, i,j,kktóre spełniają relacje.

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

(Są też ich tabele na stronie Wikipedii ).

Innymi słowy, każda wyimaginowana jednostka jest kwadratem do -1, a iloczyn dwóch różnych urojonych jednostek jest pozostałą trzecią, w +/-zależności od tego, czy (i,j,k)przestrzegany jest cykliczny porządek (tj. Reguła po prawej stronie ). Tak więc kolejność mnożenia ma znaczenie.

Czwartorzęd ogólny jest liniową kombinacją części rzeczywistej i trzech urojonych jednostek. Tak więc jest opisany czterema liczbami rzeczywistymi (a,b,c,d).

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

Możemy więc pomnożyć dwa czwarte przy użyciu właściwości rozdzielającej, uważając, aby pomnożyć jednostki we właściwej kolejności i w wyniku grupować podobne terminy.

(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

Patrząc w ten sposób, mnożenie czwartorzędu może być postrzegane jako mapa od pary 4-krotek do pojedynczego 4-krotek, o co proszono cię zaimplementować.

Format

Powinieneś napisać program lub funkcję o nazwie . Program powinien pobrać dane wejściowe ze STDIN i wydrukować wynik. Funkcja powinna przyjmować dane wejściowe funkcji i zwracać (nie drukować) dane wyjściowe.

Formaty wejściowe i wyjściowe są elastyczne. Dane wejściowe to osiem liczb rzeczywistych (współczynniki dla dwóch czwartorzędów), a dane wyjściowe składają się z czterech liczb rzeczywistych. Dane wejściowe mogą składać się z ośmiu liczb, dwóch list czterech liczb, macierzy 2x4 itp. Format wejścia / wyjścia nie musi być taki sam. Kolejność (1,i,j,k)współczynników zależy od Ciebie.

Współczynniki mogą być ujemne lub niecałe. Nie martw się o prawdziwą precyzję lub przepełnienie.

Zbanowany: funkcja lub typy specjalnie dla czwartorzędów lub ich odpowiedników.

Przypadki testowe

Są w (1,i,j,k)formacie współczynnika.

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

Wdrożenie referencyjne

W Pythonie, jako funkcja:

#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
źródło

Odpowiedzi:

4

CJam, 49 45 39 bajtów

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

W powyższym zastosowano znak karetki i notację M., ponieważ kod zawiera znaki niedrukowalne.

Kosztem dwóch dodatkowych bajtów można uniknąć tych znaków:

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

Możesz wypróbować tę wersję online: interpreter CJam

Przypadki testowe

Aby obliczyć (a + bi + cj + dk) * (e + fi + gj + hk), użyj następujących danych wejściowych:

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

Wyjście będzie

[ z y x w ]

co odpowiada ćwiartce 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]

Jak to działa

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
źródło
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]

Pobiera dwie listy A,Bw [1,i,j,k]kolejności i zwraca wynik w tym samym formacie.

Kluczową ideą jest to, że [1,i,j,k]odpowiadając indeksom [0,1,2,3], otrzymujesz indeks produktu (do podpisu) poprzez XOR'owanie indeksów. Tak więc, warunki umieszczane w indeksie pto te, które indeksują XOR p, a zatem są produktami A[m]*B[m^p].

Pozostaje tylko dopracować znaki. Najkrótszym sposobem, jaki znalazłem, było po prostu zakodowanie ich w magiczny ciąg. 16 możliwości dla (m,p)jest zamienianych na liczby 0na 15as p+4*m. Liczba 14672binarna ma 1w miejscach, w których -1potrzebne są znaki. Przesuwając ją o odpowiednią liczbę miejsc, a 1lub kończy 0się na ostatniej cyfrze, czyniąc liczbę nieparzystą lub parzystą, i tak (-1)**jest albo, 1albo -1w razie potrzeby.

xnor
źródło
Część XOR jest czystym geniuszem.
Dennis
3

Python - 90 75 72 69

Czysty Python, bez bibliotek - 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]

Prawdopodobnie bardzo trudno jest skrócić to „domyślne” rozwiązanie w Pythonie. Ale jestem bardzo ciekawy, co inni mogą wymyślić. :)


Korzystanie z NumPy - 75 72 69:

Ponieważ dane wejściowe i wyjściowe są dość elastyczne, możemy użyć niektórych funkcji NumPy i wykorzystać reprezentację wektora skalarnego :

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

Argumenty wejściowe si tsą częściami skalarnymi dwóch czwartorzędów (części rzeczywiste) pi qsą odpowiednimi częściami wektorowymi (jednostki urojone). Dane wyjściowe to lista zawierająca część skalarną i część wektorową wynikowego czwartorzędu, przy czym ten ostatni jest reprezentowany jako tablica NumPy.

Prosty skrypt testowy:

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(...)będące referencyjną realizacją PO).

Wynik:

[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
źródło
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]

Przeniesienie go do Haskell oszczędza nam kilka znaków;)

ThreeFx
źródło
2

Mathematica 83 50

Prawdopodobnie można grać w golfa więcej ..

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

Spacje i znaki nowej linii nie są liczone i nie są potrzebne.

Stosowanie:

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


EDYCJA Jak to działa.

Funkcja Mathematica Permutationsdokonuje wszystkich możliwych permutacji #2(drugiego argumentu). Istnieje 24 permutacji, ale wystarczy {e,f,g,h}, {f,e,h,g}, {g,h,e,f}, i {h,g,f,e}. Są to pierwsze, 8., 17. i 24. permutacje. Więc kod

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

dokładnie wybiera je z permutacji drugiego argumentu i zwraca je jako macierz. Ale wtedy nie mają jeszcze właściwego znaku. Kod p[{-1,1,-1,1}][[1;;3]]zwraca macierz 3x4 z poprawnym znakiem. Poprzedzamy to {1,1,1,1}za pomocą Join, i zwykłe mnożenie ( Timeslub tak jak tutaj, po prostu zapisywanie ich po sobie) między dwiema macierzami tworzy mnożenie element po elemencie w Mathematica.

Tak więc w końcu wynik

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

jest matrycą

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

Wykonanie mnożenia macierzy między {a,b,c,d}(pierwszym argumentem #1) a poprzednią macierzą daje pożądany wynik.



EDYCJA 2 Krótszy kod

Zainspirowany kodem Falko w języku Python, podzieliłem czwartorzęd na część skalarną i wektorową i wykorzystuję wbudowane polecenie Mathematica Crossdo obliczenia iloczynu krzyżowego części wektorowych:

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

Stosowanie:

f[a,{b,c,d},e,{f,g,h}]        (* => {x,w,y,z}   *)
freddieknets
źródło
Czy możesz wyjaśnić, jak to działa? Co to 1, 8, 17, 24jest
xnor
1

Python, 94

Najprostszy sposób nie jest zbyt długi.

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.
źródło
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
źródło
1

Lua - 99

Może również.

_,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 ()” Lui uwalnia elementy tabeli. Tak więc tabela „arg” jest miejscem, w którym przechowywane są wszystkie dane z wiersza poleceń (w tym arg[0]nazwa pliku programu, jest odrzucana).

AndoDaan
źródło
1

Python, 58 56 znaków

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

Biorę bardzo liberalne wykorzystania wejścia / wyjścia pomieszczenia Format wiggle. Dane wejściowe to 4 liczby zespolone, zakodowane w następujący sposób:

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

Wyprowadza parę liczb zespolonych w podobnym formacie, pierwsza z nich koduje wartość rzeczywistą i iczęść, druga koduje część ji k.

Aby zobaczyć, jak to działa, należy zauważyć, że pierwszy czwartorzędowy jest, x+y*ja drugi jest z+w*j. Wystarczy ocenić (x+y*j)*(z+w*j)i zdać sobie sprawę, że j*t= conj(t)*jdla dowolnej liczby urojonej t.

Keith Randall
źródło
Bardzo mądry! Czy wiesz, dlaczego czwartorzędy zdają się mnożyć jak liczby zespolone ze złożonymi współczynnikami, jak wynika z Twojego wyrażenia?
xnor
Nieważne, teraz rozumiem z twojego wyjaśnienia, jak ii jzachowują się jak wewnętrzne i zewnętrzne złożone współczynniki. Jak fascynujące!
xnor
To zabawne, jak wywołania sprzężenia zajmują ponad 2/5 twoich znaków. Myślę, że możesz golić char za każdym razem (2*w.real-w). abs(w)**2/wdziałałoby, ale dla 0. Może nawet exec z podstawieniem łańcucha byłoby tego warte? `
xnor
1

Whispers v2 , 396 bajtów

> 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

Wypróbuj online!

Pobiera dane wejściowe w formularzu

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

i wyniki jako

w
x
y
z

q=w+xi+yj+zk

Drzewo struktury tej odpowiedzi to:

drzewo

Znaczna część tej odpowiedzi pochodzi z dwóch głównych błędów w Szeptach:

  • Brak funkcji do odwrócenia tablicy
  • Wykorzystanie zbiorów w obliczeniach iloczynu kartezjańskiego

Dlatego możemy podzielić kod na 3 sekcje.

Jak to działa

Użyjemy następujących definicji dla jasności i zwięzłości:

q=a+bi+cj+dk
p=e+fi+gj+hk
r=w+xi+yj+zk,(qp=r)
A=[a,b,c,d]
B=[e,f,g,h]
C=[w,x,y,z]

AB

Pierwsza sekcja jest zdecydowanie najdłuższa i rozciąga się od linii 1 do linii 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

BABBA

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

BBBB2B4

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

[f,e]

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

(formowanie [h,g]B1,B2,B3B4BTATA4

AT=[a,b,c,d,a,b,c,d,a,b,c,d,a,b,c,d]
BT=[e,f,g,h,f,e,h,g,g,h,e,f,h,g,f,e]

BTATqp

Część 2: Znaki i produkty

ATBTqp

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

SAT,BTS[[a,e,1],[b,f,1],,[e,f,1],[d,e,1]]D=[ae,bf,,ef,de]

Sekcja 3: Partycje i sumy końcowe.

qpqp

> 4

54DD

Cairney Coheringaahing
źródło