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
бере елемент кільця і відображає його у відповідне ціле число. Я роблю це, отримуючи список коефіцієнтів многочлена, і для кожного ненульового коефіцієнта результат збільшується на 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
), тому я збираюся залишити це як коментар тут.