Умножить кватернионы


13

Напишите именованную функцию или программу, которая вычисляет произведение кватернионов двух кватернионов. Используйте как можно меньше байтов.

Кватернионы

Кватернионы - это расширение действительных чисел, которое дополнительно расширяет комплексные числа. Вместо одной мнимой единицы iкватернионы используют три мнимые единицы, i,j,kкоторые удовлетворяют отношениям.

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

(Там также таблицы этих на странице Википедии .)

Словом, каждая мнимая единица возводится в квадрат -1, а произведение двух разных мнимых единиц является оставшейся третьей единицей в +/-зависимости от того, (i,j,k)соблюдается ли циклический порядок (т. Е. Правило правой руки ). Итак, порядок умножения имеет значение.

Общий кватернион - это линейная комбинация действительной части и трех мнимых единиц. Итак, это описывается четырьмя действительными числами (a,b,c,d).

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

Таким образом, мы можем умножить два кватерниона, используя свойство дистрибутива, стараясь умножить единицы в правильном порядке и сгруппировав одинаковые термины в результате.

(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-х кортежей до одного 4-кортежа, что вам и нужно реализовать.

Формат

Вы должны написать либо программу, либо именованную функцию . Программа должна принимать входные данные из STDIN и распечатывать результат. Функция должна принимать входные данные функции и возвращать (не печатать) выходные данные.

Форматы ввода и вывода являются гибкими. Входные данные представляют собой восемь действительных чисел (коэффициенты для двух кватернионов), а выходные данные состоят из четырех действительных чисел. Входными данными могут быть восемь чисел, два списка из четырех чисел, матрица 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, поскольку код содержит непечатаемые символы.

Этими символами можно избежать за счет двух дополнительных байтов:

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

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]

Принимает два списка A,Bпо [1,i,j,k]порядку и возвращает результат в том же формате.

Основная идея заключается в том, что с [1,i,j,k]соответствующими индексами [0,1,2,3]вы получаете индекс продукта (с точностью до знака), XOR'ing индексы. Таким образом, термины, помещаемые в индекс, p- это те, кто индексирует XOR pи, следовательно, являются продуктами A[m]*B[m^p].

Осталось только заставить знаки работать. Самый короткий путь, который я нашел, - просто закодировать их в волшебную строку. 16 возможностей для (m,p)превращаются в числа 0для 15как p+4*m. Число 14672в двоичном виде есть 1в местах, где -1нужны знаки. Сдвигая его на соответствующее число мест, a 1или 0заводится в последнюю цифру, делая число нечетным или четным, и так (-1)**или 1иначе или по -1мере необходимости.


Часть XOR - чистый гений.
Деннис

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

Входные аргументы sи tявляются скалярными частями двух кватернионов (действительными частями) pи 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(...)являясь эталонной реализацией ФП.)

Выход:

[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(второй аргумент). Есть 24 перестановок, но нам нужны только {e,f,g,h}, {f,e,h,g}, {g,h,e,f}, и {h,g,f,e}. Это первая, 8-я, 17-я и 24-я перестановки. Итак, код

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

точно выбирает их из перестановок второго аргумента и возвращает их в виде матрицы. Но тогда у них пока нет правильного знака. Код p[{-1,1,-1,1}][[1;;3]]возвращает матрицу 3x4 с правильным знаком. Мы добавляем его с {1,1,1,1}помощью 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) и предыдущей матрицей дает желаемый результат.



РЕДАКТИРОВАТЬ 2 более короткий код

Вдохновленный Python-кодом Falko, я разделил кватернион на скалярную и векторную части и использую встроенную команду 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

1

Питон, 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

Он выводит пару комплексных чисел в аналогичном формате, первая из пары кодирует действительное число и iчасть, вторая кодируетj и kчасти.

Чтобы увидеть это работает, обратите внимание, что первый кватернион есть, x+y*jа второй есть z+w*j. Просто оцените (x+y*j)*(z+w*j)и поймите это j*t= conj(t)*jдля любого воображаемого числа t.


Очень умно! Знаете ли вы, почему кватернионы, кажется, умножаются как комплексные числа с комплексными коэффициентами, как это видно из вашего выражения?
xnor

Неважно, теперь я понимаю из твоего объяснения, как iи как jдействовать как внутренние и внешние сложные коэффициенты. Как увлекательно!
xnor

Забавно, что мошеннические звонки занимают более 2/5 ваших символов. Я думаю, что вы можете побрить чар каждый из них (2*w.real-w). abs(w)**2/wбудет работать, но за 0. Может быть, даже exec с подстановкой строк будет стоить? `
xnor

1

Whispers 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знак равновес+Икся+YJ+ZК

Структура дерева этого ответа:

tree

Хорошая часть этого ответа происходит от двух основных недостатков в Whispers:

  • Нет функции для обратного массива
  • Использование множеств в вычислении декартового произведения

Поэтому мы можем разбить код на 3 раздела.

Как это устроено

Мы будем использовать следующие определения для ясности и краткости:

Qзнак равноa+бя+сJ+dК
пзнак равное+ея+граммJ+часК
рзнак равновес+Икся+YJ+ZК,(Qпзнак равнор)
Aзнак равно[a,б,с,d]
Взнак равно[е,е,грамм,час]
Сзнак равно[вес,Икс,Y,Z]

Раздел 1. Перестановка A и В

Первый раздел является самым длинным и простирается от линии 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

Основная цель этого раздела - переставить В так что простое поэлементное умножение между A и Ввозможно. Существует четыре различных вариантаВ умножить элементы A с:

В1знак равно[е,е,грамм,час]
В2знак равно[е,е,час,грамм]
В3знак равно[грамм,час,е,е]
В4знак равно[час,грамм,е,е]

Второй вход, В, хранится в строке 6 . Затем мы разделимВ вниз по центру, как каждое возможное расположение Всгруппированы по парам. Чтобы поменять эти пары (чтобы получить правильные заказы вВ2 и В4), мы берем первый и последний элемент, затем объединяем их в обратном порядке:

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

(формирование [е,е]) и

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

(формирование [час,грамм]). Теперь у нас есть все половинки, необходимые для формирования договоренностей, поэтому мы объединяем их вместе, чтобы сформироватьВ1,В2,В3 и В4, Наконец, мы объединяем эти четыре договоренности вместе, чтобы сформироватьВT, Затем мы быстро сделаемAT, определяется как A повторный 4 раз:

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.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.