Multiply Quaternions
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]
Takes two lists A,B
in [1,i,j,k]
order and returns a result in the same format.
The key idea is that with [1,i,j,k]
corresponding to indices [0,1,2,3]
, you get the product's index (up to sign) by XOR'ing the indices. So, the terms that get placed in index p
are those who indices XOR to p
, and are thus the products A[m]*B[m^p]
.
It only remains to make the signs work out. The shortest way I found was to simply code them into a magic string. The 16 possibilities for (m,p)
are turned into numbers 0
to 15
as p+4*m
. The number 14672
in binary has 1
's at the places where -1
signs are needed. By shifting it the appropriate number of places , a 1
or 0
winds up at the last digit, making the number odd or even, and so (-1)**
is either 1
or -1
as needed.
CJam, 49 45 39 bytes
"cM-^\M-^G-^^KM-zP"256bGbq~m*f{=:*}4/{:-W*}/W*]`
The above uses caret and M notation, since the code contains unprintable characters.
At the cost of two additional bytes, those characters can be avoided:
6Z9C8 7YDXE4BFA5U]q~m*f{=:*}4/{:-W*}/W*]`
You can try this version online: CJam interpreter
Test cases
To calculate (a + bi + cj + dk) * (e + fi + gj + hk)
, use the following input:
[ d c b a ] [ h g f e ]
The output will be
[ z y x w ]
which corresponds to the 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]
How it works
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. ";
Python - 90 75 72 69
Pure Python, no libraries - 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]
It's probably pretty hard to shorten this "default" solution in Python. But I'm very curious as to what other might come up with. :)
Using NumPy - 75 72 69:
Well, since the input and output are rather flexible, we can use some NumPy functions and exploit the scalar-vector representation:
import numpy
m=lambda s,p,t,q:[s*t-sum(p*q),s*q+t*p+numpy.cross(p,q)]
Input arguments s
and t
are the scalar parts of the two quaternions (the real parts) and p
and q
are the corresponding vector parts (the imaginary units). Output is a list containing scalar part and vector part of the resulting quaternion, the latter being represented as NumPy array.
Simple test script:
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(...)
being the OP's reference implementation.)
Output:
[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])]