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


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 q⋅p

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 q⋅p. 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 q⋅p, with signs, we need to split it into 4 parts (i.e. the four factorised coefficients of q⋅p), 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.