GAP , 368 байт
Для математиков это умножение в кольце многочленов F_2 [x], отождествление многочленов с натуральными числами путем оценки при x = 2 как многочлена над Z.
Конечно, давайте сделаем это! (это всего лишь лёгкая игра в гольф, смысл был больше двигаться в F 2 [x] и делать вычисления больше, чем любая попытка быть выигрышной)
Вот код
f:=function(i,j)R:=PolynomialRing(GF(2));x:=IndeterminatesOfPolynomialRing(R);x:=x[1];a:=function(i)local n,r;r:=0*x;while not i=0 do n:=0;while 2^n<=i do n:=n+1;od;n:=n-1;r:=r+x^n;i:=i-2^n;od;return r;end;b:=function(r)local c,i,n;i:=0;n:=0;for c in CoefficientsOfUnivariatePolynomial(r) do if c=Z(2)^0 then n:=n+2^i;fi;i:=i+1;od;return n;end;return b(a(i)*a(j));end;
Вот негольфированный код с объяснением:
xor_multiplication:=function(i,j)
R:=PolynomialRing(GF(2));
x:=IndeterminatesOfPolynomialRing(R);
x:=x[1];
to_ring:=function(i)
local n,r;
r:=0*x;
while not i=0 do
n:=0;
while 2^n<=i do
n:=n+1;
od;
n:=n-1;
r:=r+x^n;
i:=i-2^n;
od;
return r;
end;
to_ints:=function(r)
local c,i,n;
i:=0;n:=0;
for c in CoefficientsOfUnivariatePolynomial(r) do
if c=Z(2)^0 then
n:=n+2^i;
fi;
i:=i+1;
od;
return n;
end;
return to_ints( to_ring(i)*to_ring(j));
end;
Итак, во-первых, мы создадим одномерное кольцо многочленов над полем F 2 и назовем его R
. Обратите внимание, что GF(2)
это F 2 в GAP.
R:=PolynomialRing(GF(2));
Далее мы собираемся присвоить переменную GAP x
неопределенному кольцу R
. Теперь, когда я говорю x
в GAP, система будет знать, что я говорю о неопределенности кольца R
.
x:=IndeterminatesOfPolynomialRing(R);
x:=x[1];
Далее у нас есть две функции, которые являются обратными отображениями друг друга. Эти карты обе на, но они не сохраняют структуру, поэтому я не мог найти лучший способ реализовать их в GAP. Там почти наверняка есть лучший способ, если вы знаете это, пожалуйста, прокомментируйте!
Первая карта to_ring
берет целое число и отображает его в соответствующий элемент кольца. Он делает это с помощью преобразования в двоичный алгоритм, где все, 1
что должно появиться в двоичном коде, заменяется на « x^n
где» n
- подходящая степень, которую 2 взяла бы, если бы число было действительно двоичным.
to_ring:=function(i)
local n,r;
r:=0*x; # initiate r to the zero element of R
while not i=0 do # this is a modified binary algorithm
n:=0;
while 2^n<=i do
n:=n+1;
od;
n:=n-1;
r:=r+x^n;
i:=i-2^n;
od;
return r;
end;
Следующая функция полностью изменяет это. to_ints
берет элемент ring и отображает его в соответствующее ему целое число. Я делаю это, получая список коэффициентов полинома, и для каждого ненулевого коэффициента результат увеличивается на 2 ^ n, так же, как мы конвертируем двоичный код в десятичный.
to_ints:=function(r)
local c,i,n;
i:=0;n:=0;
for c in CoefficientsOfUnivariatePolynomial(r) do
if c=Z(2)^0 then
# ^-- Right here you'll notice that the Z(2) is basically '1' in GF(2). So Z(2)^0 ~ 1 and Z(2)*0 ~ 0
# effectively, this line checks for nonzero coefficients
n:=n+2^i;
fi;
i:=i+1;
od;
return n;
end;
На последнем этапе мы вызываем эти функции. Мы берем два целочисленных ввода, преобразуем их в элементы в кольце R
, затем умножаем эти элементы вместе и отправляем произведение обратно в целые числа.
return to_ints( to_ring(i)*to_ring(j));
PCLMULQDQ
из расширения CLMUL. К сожалению, я получил отрицательный отзыв за свои знания о наборе команд x86 (связан сPEXT/PDEP
), поэтому я оставлю это здесь в качестве комментария.