四元数の乗算


13

2つの四元数の四元数積を計算する名前付き関数またはプログラムを作成します。できるだけ少ないバイトを使用してください。

四元数

四元数は、複素数をさらに拡張する実数の拡張です。i四元数は、単一の虚数単位ではなくi,j,k、関係を満たす3つの虚数単位を使用します。

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

Wikipediaページにもこれらの表があります。)

言い換えると、各虚数単位はに2乗し-1、2つの異なる虚数単位の積は残りの3番目の単位であり+/-、循環次数(i,j,k)が尊重されるかどうかに依存します(つまり、右手則)。したがって、乗算の順序が重要です。

一般的なクォータニオンは、実数部と3つの虚数単位の線形結合です。したがって、4つの実数で記述されます(a,b,c,d)

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

そのため、分配プロパティを使用して2つのクォータニオンを乗算し、正しい順序で単位を乗算するように注意し、結果で類似の用語をグループ化できます。

(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

このように見ると、四元数の乗算は、4タプルのペアから1つの4タプルへのマップとして見ることができます。

フォーマット

プログラムまたは名前付き関数のいずれかを作成する必要があります。プログラムは、STDINから入力を受け取り、結果を出力する必要があります。関数は関数の入力を受け取り、出力を返す(印刷しない)必要があります。

入力および出力形式は柔軟です。入力は8つの実数(2つの四元数の係数)であり、出力は4つの実数で構成されます。入力は8つの数字、4つの数字の2つのリスト、2x4マトリックスなどです。入力/出力形式は同じである必要はありません。(1,i,j,k)係数の順序はユーザー次第です。

係数は、負の値でも、全体以外でもかまいません。実際の精度やオーバーフローについて心配する必要はありません。

禁止:クォータニオンまたは同等の機能またはタイプ。

テストケース

これらは(1,i,j,k)係数形式です。

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

参照実装

Pythonでは、関数として:

#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

回答:


4

CJam、49 45 39バイト

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

上記のコードでは印刷できない文字が含まれているため、キャレットとM表記を使用しています。

追加の2バイトのコストで、これらの文字を回避できます。

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

このバージョンをオンラインで試すことができます:CJamインタープリター

テストケース

を計算する(a + bi + cj + dk) * (e + fi + gj + hk)には、次の入力を使用します。

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

出力は

[ z y x w ]

これはクォータニオンに対応し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]

使い方

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

パイソン(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]

2つのリストA,B[1,i,j,k]順番に受け取り、同じ形式で結果を返します。

重要な考え方は、[1,i,j,k]インデックス[0,1,2,3]に対応するインデックスをXORすることで、製品のインデックス(符号まで)を取得することです。したがって、インデックスに配置される用語は、pXORにインデックスを付けるものpであり、したがって製品A[m]*B[m^p]です。

兆候がうまくいくようにするだけです。私が見つけた最短の方法は、単純にそれらを魔法の文字列にコード化することでした。の16個の可能性(m,p)は、の数値0に変換さ15p+4*mます。14672バイナリの数字は1-1記号が必要な場所にあります。それに場所の適切な数をシフトすることにより、1または0奇数または偶数を作り、最後の桁に巻き取る、など(-1)**のいずれかである1か、-1必要に応じ。


XORパートは純粋な天才です。
デニス14

3

パイソン-90 75 72 69

純粋なPython、ライブラリなし-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]

Pythonでこの「デフォルト」ソリューションを短縮することはおそらくかなり難しいでしょう。しかし、私は他の人が何を思いつくかについて非常に興味があります。:)


NumPyの使用-75 72 69:

さて、入力と出力はかなり柔軟なので、いくつかのNumPy関数を使用して、スカラーベクトル表現を活用できます。

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

入力引数sand tは、2つの四元数のスカラー部分(実数部)およびpand qは、対応するベクトル部分(虚数単位)です。出力は、結果のクォータニオンのスカラー部分とベクトル部分を含むリストです。後者はNumPy配列として表されます。

簡単なテストスクリプト:

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(...)OPのリファレンス実装です。)

出力:

[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

ハスケル、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]

Haskellに移植すると、いくつかの文字を節約できます;)


2

Mathematica 83 50

おそらくもっとゴルフができます。

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

スペースと改行はカウントされず、必要ありません。

使用法:

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


編集 これの仕組み。

Mathematica関数Permutations#2(2番目の引数)のすべての可能な順列を作成します。そこ24個の順列がありますが、我々は唯一の必要{e,f,g,h}{f,e,h,g}{g,h,e,f}、と{h,g,f,e}。これらは、1番目、8番目、17番目、および24番目の順列です。だからコード

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

2番目の引数の順列からこれらを正確に選択し、行列として返します。しかし、その後、彼らはまだ正しいサインを持っていません。コードp[{-1,1,-1,1}][[1;;3]]は、正しい符号を持つ3x4行列を返します。を{1,1,1,1}使用して先頭に追加し、2つの行列間でJoin通常の乗算​​(Times、または単に次のように書き込むことでこの場合のように)を行うと、Mathematicaで要素ごとの乗算が行われます。

最後に、結果

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

は行列です

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

{a,b,c,d}(最初の引数#1)と前の行列の間で行列乗算を行うと、望ましい結果が得られます。



EDIT 2短いコード

FalkoのPythonコードに触発されて、クォータニオンをスカラー部分とベクトル部分に分割し、Mathematicaの組み込みコマンドCrossを使用してベクトル部分の外積を計算します。

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

使用法:

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

これがどのように機能するか説明してもらえますか?なに1, 8, 17, 24
xnor 14

1

Python、94

最も簡単な方法は長すぎません。

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

ルア-99

かもしれない。

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

Luaの「unpack()」はテーブルの要素を解放します。したがって、テーブル「arg」は、すべてのコマンドライン入力が格納される場所です(arg[0]プログラムのファイル名を含む、それは破棄されます)。


1

Python、58 56文字

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

私は、入出力形式の小刻みの部屋を非常に自由に使用しています。入力は、次のようにエンコードされた4つの複素数です。

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

同様の形式で複素数のペアを出力します。ペアの最初は実ijk部分をエンコードし、2番目は部と部分をエンコードします。

この動作を確認するには、最初のクォータニオンがx+y*jで、2番目のクォータニオンがであることに注意してくださいz+w*j。任意の虚数に対して(x+y*j)*(z+w*j)that j*t= conj(t)*jを評価して実現しtます。


非常に賢い!四元数が式から見えるように、なぜ四元数が複素数のように複素係数と乗算するように見えるのか知っていますか?
xnor 14

気にしないで、私はあなたの説明から内部および外部の複素係数としてどのようにi、そしてどのようにj振る舞うかを理解しました。なんて魅力的でしょう!
xnor 14

conj呼び出しがどのようにあなたのcharの2/5以上を占めるかは面白いです。を使用してそれぞれの文字を剃ることができると思います(2*w.real-w)abs(w)**2/w動作しますが、0の場合です。たぶん、文字列置換を使用するexecでも価値がありますか?`
xnor 14

1

ささやきv2、396バイト

> 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

オンラインでお試しください!

フォームに入力を取ります

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

そして出力

w
x
y
z

表現します q=w+バツ+yj+zk

この答えの構造ツリーは次のとおりです。

木

この答えのかなりの部分は、Whispersの2つの主な欠点に由来しています。

  • 配列を反転する機能はありません
  • デカルト積の計算におけるセットの使用法

したがって、コードを3つのセクションに分割できます。

使い方

明確さと簡潔さのために、次の定義を使用します。

q=a+b+cj+dk
p=e+f+gj+hk
r=w+バツ+yj+zkqp=r
A=[abcd]
B=[efgh]
C=[wバツyz]

セクション1:順列 A そして B

最初のセクションは、行1から行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

このセクションの主な目的は、並べ替えることです B そのため、単純な要素ごとの乗算 A そして B可能だ。の4つの異なる配置がありますB の要素を乗算する A で:

B1=[efgh]
B2=[fehg]
B3=[ghef]
B4=[hgfe]

2番目の入力、 B6行目に保存されます。次に分割しますB それぞれの可能な配置として、真ん中を Bペアでグループ化されます。これらのペアを逆にするには(正しい順序を取得するにはB2 そして B4)、最初と最後の要素を取得し、それらを逆の順序で連結します:

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

(フォーミング [fe])および

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

(フォーミング [hg])。配置を形成するために必要な半分がすべて揃ったので、それらを連結して形成しますB1B2B3 そして B4。最後に、これら4つの配置を連結して、BT。それからすぐにATとして定義 A 繰り返した 4 回:

AT=[abcdabcdabcdabcd]
BT=[efghfehgghefhgfe]

の各要素 BT の対応する要素が乗算されます AT、(signless)値を取得します qp

セクション2:サインと製品

セクション1で述べたように、 AT そして BT の各係数の符号のない(つまり正の)値に対応する qp。単純な配列のハードコーディングよりも短い明らかなパターンは符号に見つからないため、配列をハードコーディングします。

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

この配列を呼び出します S(サイン)。次に、各要素を一緒に圧縮しますATBT そして S そして、各サブアレイの積を取る [[ae1][bf1][ef1][de1]]D=[aebfefde]

セクション3:パーティションと最終合計。

の係数の配列ができたら qp、記号を使用して、4つの部分に分割する必要があります(つまり、 qp)、合計を取ります。これにより、ゴルフの唯一の機会が見つかります。

> 4

行に4よりもむしろ26が6回、それを移動させることによってバイトを保存するたびに使用されます、。残念ながら、これには910に変更するバイトがかかるため、5バイトが保存されます。次のセクションでは、サイズのスライスを取ります4 正面から D、各スライスを対応する行に保存し、の短縮リストを渡す D。4つのスライスが取得されたら、すべてを出力する前に、それぞれの合計を取得します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.