Multiplier les quaternions


13

Écrivez une fonction ou un programme nommé qui calcule le produit quaternion de deux quaternions. Utilisez le moins d'octets possible.

Quaternions

Les quaternions sont une extension des nombres réels qui étend encore les nombres complexes. Plutôt qu'une seule unité imaginaire i, les quaternions utilisent trois unités imaginaires i,j,kqui satisfont les relations.

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

(Il y a aussi des tableaux de ceux-ci sur la page Wikipedia .)

En d'autres termes, chaque unité imaginaire est au carré -1, et le produit de deux unités imaginaires différentes est le troisième restant avec un +/-selon que l'ordre cyclique (i,j,k)est respecté (c'est -à-dire la règle de droite) ). Donc, l'ordre de multiplication est important.

Un quaternion général est une combinaison linéaire d'une partie réelle et des trois unités imaginaires. Ainsi, il est décrit par quatre nombres réels (a,b,c,d).

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

Ainsi, nous pouvons multiplier deux quaternions en utilisant la propriété distributive, en prenant soin de multiplier les unités dans le bon ordre et en regroupant les termes similaires dans le résultat.

(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

Vu de cette façon, la multiplication du quaternion peut être considérée comme une carte d'une paire de 4-tuples à un seul 4-tuples, ce que vous êtes invité à mettre en œuvre.

Format

Vous devez écrire un programme ou une fonction nommée . Un programme doit prendre les entrées de STDIN et imprimer le résultat. Une fonction doit accepter les entrées de fonction et retourner (pas imprimer) une sortie.

Les formats d'entrée et de sortie sont flexibles. L'entrée est huit nombres réels (les coefficients pour deux quaternions), et la sortie se compose de quatre nombres réels. L'entrée peut être huit nombres, deux listes de quatre nombres, une matrice 2x4, etc. Le format d'entrée / sortie n'a pas à être le même. L'ordre des (1,i,j,k)coefficients dépend de vous.

Les coefficients peuvent être négatifs ou non entiers. Ne vous inquiétez pas de la précision réelle ou des débordements.

Interdit: Fonction ou types spécifiquement pour les quaternions ou équivalents.

Cas de test

Celles-ci sont au (1,i,j,k)format coefficient.

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

Implémentation de référence

En Python, comme fonction:

#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

Réponses:


4

CJam, 49 45 39 octets

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

Ce qui précède utilise le signe d'insertion et la notation M, car le code contient des caractères non imprimables.

Au prix de deux octets supplémentaires, ces caractères peuvent être évités:

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

Vous pouvez essayer cette version en ligne: CJam interpreter

Cas de test

Pour calculer (a + bi + cj + dk) * (e + fi + gj + hk), utilisez l'entrée suivante:

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

La sortie sera

[ z y x w ]

ce qui correspond au 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]

Comment ça fonctionne

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]

Prend deux listes A,Bdans l' [1,i,j,k]ordre et renvoie un résultat au même format.

L'idée clé est qu'avec la [1,i,j,k]correspondance avec les indices [0,1,2,3], vous obtenez l'indice du produit (à signer) en XOR 'les indices. Ainsi, les termes qui sont placés dans l'index psont ceux qui indexent XOR enp et sont donc les produitsA[m]*B[m^p] .

Il ne reste plus qu'à faire fonctionner les signes. Le moyen le plus court que j'ai trouvé était de simplement les coder en une chaîne magique. Les 16 possibilités pour (m,p)sont transformées en nombres 0en 15as p+4*m. Le nombre 14672en binaire a 1aux endroits où les -1signes sont nécessaires. En le décalant du nombre approprié de places, a 1ou se 0termine au dernier chiffre, ce qui rend le nombre impair ou pair, et ainsi (-1)**est soit 1ou -1selon les besoins.


La partie XOR est un pur génie.
Dennis

3

Python - 90 75 72 69

Pure Python, pas de bibliothèques - 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]

Il est probablement assez difficile de raccourcir cette solution "par défaut" en Python. Mais je suis très curieux de savoir ce que d'autres pourraient proposer. :)


Utilisation de NumPy - 75 72 69:

Eh bien, comme l'entrée et la sortie sont plutôt flexibles, nous pouvons utiliser certaines fonctions NumPy et exploiter la représentation scalaire-vectorielle :

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

Les arguments d'entrée set tsont les parties scalaires des deux quaternions (les parties réelles) et pet qsont les parties vectorielles correspondantes (les unités imaginaires). La sortie est une liste contenant une partie scalaire et une partie vectorielle du quaternion résultant, ce dernier étant représenté par un tableau NumPy.

Script de test simple:

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(...) étant la mise en œuvre de référence du PO.)

Production:

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

Le porter sur Haskell nous permet d'économiser quelques caractères;)


2

Mathematica 83 50

Peut probablement être joué au golf plus ..

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

Les espaces et les nouvelles lignes ne comptent pas et ne sont pas nécessaires.

Usage:

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


EDIT Comment cela fonctionne.

La fonction Mathematica Permutationseffectue toutes les permutations possibles de #2(le deuxième argument). Il y a 24 permutations, mais nous avons seulement besoin {e,f,g,h}, {f,e,h,g}, {g,h,e,f}et {h,g,f,e}. Ce sont les première, 8e, 17e et 24e permutations. Donc, le code

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

les sélectionne exactement parmi les permutations du deuxième argument et les renvoie sous forme de matrice. Mais ils n'ont pas encore le bon signe. Le code p[{-1,1,-1,1}][[1;;3]]renvoie une matrice 3x4 avec le signe correct. Nous le ajoutons {1,1,1,1}en utilisant Joinet en faisant une multiplication normale (Times , ou comme c'est le cas ici en les écrivant juste après l'autre) entre deux matrices, élément par élément dans Mathematica.

Alors finalement, le résultat de

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

est la matrice

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

Faire une multiplication matricielle entre {a,b,c,d}(le premier argument #1) et l'ancienne matrice donne le résultat souhaité.



EDIT 2 Code abrégé

Inspiré par le code Python de Falko, j'ai divisé le quaternion en une partie scalaire et une partie vectorielle, et j'utilise la commande intégrée de Mathematica Crosspour calculer le produit croisé des parties vectorielles:

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

Usage:

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

Pourriez-vous expliquer comment cela fonctionne? Quels sont-ils 1, 8, 17, 24?
xnor

1

Python, 94

La manière la plus simple n'est pas trop longue.

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

Peut-être aussi.

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

Le "unpack ()" de Lua libère les éléments d'une table. Ainsi, la table 'arg' est l'endroit où toutes les entrées de ligne de commande sont stockées (y compris arg[0]le nom de fichier du programme, il est supprimé).


1

Python, 58 56 caractères

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

Je prends une utilisation très libérale de la marge de manœuvre du format d'entrée / sortie. Les entrées sont 4 nombres complexes, codés ainsi:

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

Il génère une paire de nombres complexes dans un format similaire, le premier de la paire code le réel et la ipartie, le second code le jet les kparties.

Pour voir cela fonctionne, notez que le premier quaternion est x+y*jet le second est z+w*j. Il suffit d'évaluer (x+y*j)*(z+w*j)et de réaliser que j*t= conj(t)*jpour tout nombre imaginaire t.


Très intelligent! Savez-vous pourquoi les quaternions semblent se multiplier comme des nombres complexes avec des coefficients complexes, comme il ressort de votre expression?
2014

Peu importe, je comprends maintenant à partir de votre explication comment iet jagissez comme des coefficients complexes internes et externes. Comme c'est fascinant!
xnor

C'est amusant de voir comment les appels conj occupent plus des 2/5 de vos caractères. Je pense que vous pouvez vous raser un char en utilisant chacun (2*w.real-w). abs(w)**2/wfonctionnerait mais pour 0. Peut-être que même un exécutable avec substitution de chaîne en vaudrait la peine? `
xnor

1

Whispers v2 , 396 octets

> 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

Essayez-le en ligne!

Prend entrée dans le formulaire

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

et sorties comme

w
x
y
z

représenter q=w+Xje+yj+zk

L'arbre de structure de cette réponse est:

tree

Une bonne partie de cette réponse provient de deux défauts principaux dans Whispers:

  • Aucune fonction pour inverser un tableau
  • L'utilisation des ensembles dans le calcul du produit cartésien

Par conséquent, nous pouvons diviser le code en 3 sections.

Comment ça fonctionne

Nous utiliserons les définitions suivantes pour plus de clarté et de concision:

q=une+bje+cj+k
p=e+Fje+gj+hk
r=w+Xje+yj+zk,(qp=r)
UNE=[une,b,c,]
B=[e,f,g,h]
C=[w,x,y,z]

Section 1: Permuting A and B

The first section is by far the longest, stretching from line 1 to line 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

The main purpose of this section is to permute B so that simple element-wise multiplication between A and B is possible. There are four different arrangements of B to multiply the elements of A with:

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

The second input, B, is stored on line 6. We then split B down the middle, as each possible arrangement of B is grouped in pairs. In order to reverse these pairs (to get the correct orders in B2 and B4), we take the first and last element, then concatenate them in reverse order:

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

(forming [f,e]) and

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

(forming [h,g]). We now have all the halves needed to form the arrangements, so we concatenate them together to form B1,B2,B3 and B4. Finally, we concatenate these four arrangements together to form BT. We then quickly make AT, defined as A repeated 4 times:

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]

When each element of BT is multiplied by the corresponding element in AT, we get the (signless) values in qp

Section 2: Signs and products

As said in Section 1, the values in AT and BT correspond to the signless (i.e. positive) values of each of the coefficients in qp. No obvious pattern is found in the signs that would be shorter than simply hardcoding the array, so we hardcode the array:

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

We'll call this array S (signs). Next, we zip together each element in AT,BT and S and take the product of each sub-array e.g. [[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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.