Множення XOR


33

Ваша мета полягає в тому, щоб реалізувати операцію множення XOR ( несучої ), визначену нижче, якомога менше байтів.

Якщо ми вважаємо, що бітовий XOR ( ^) є бінарним доповненням без перенесення

   101   5
^ 1001   9
  ----  
  1100  12

  5^9=12

ми можемо виконувати множення XOR @, виконуючи двійкове довге множення, але виконуючи крок додавання, не переносячи як бітовий XOR ^.

     1110  14
   @ 1101  13
    -----
     1110
       0
   1110
^ 1110 
  ------
  1000110  70

  14@13=70

(Для математиків це множення в поліноміальному кільці F_2[x], ототожнення поліномів з натуральними числами шляхом оцінки на x=2поліном над Z.)

Множення XOR комутує a@b=b@a, асоціює (a@b)@c=a@(b@c)та розподіляє по побітових XOR a@(b^c)=(a@b)^(a@c). Насправді, це унікальна така операція, яка відповідає множенню a@b=a*bколи завгодно aі bє 2подібними силами 1,2,4,8....

Вимоги

Візьміть два невід’ємні цілі числа як введення та вихід або надрукуйте їх XOR-продукт. Це повинні бути як числа або їх десяткові представлення рядків, а не їх двійкові розширення. Виграє найменше байт.

Не турбуйтеся про цілі надлишки.

Ось кілька тестових випадків, відформатованих як a b a@b.

0 1 0
1 2 2
9 0 0
6 1 6
3 3 5
2 5 10
7 9 63
13 11 127
5 17 85
14 13 70
19 1 19
63 63 1365

13
Це більш відоме як "перенесення без перенесення", до якого ви можете додати назву питання, і з великою часткою ймовірності найменший запис - це 6-байтна інструкція x86 PCLMULQDQз розширення CLMUL. На жаль, мені не вдалося визнати мої знання про встановлену раніше інструкцію x86 (Пов'язано з PEXT/PDEP), тому я збираюся залишити це як коментар тут.
Iwillnotexist Idonotexist

@IwillnotexistIdonotexist Дякую за замітку, приємно мати ім’я в Google.
xnor

Якщо це вище не "xor", ви повинні дзвонити по-іншому, як xorc або xornc ... Це не xor
RosLuP

1
@RosLuP Це не xor, це xor множення.
xnor

@boboquack Власне, я вважаю, що множення німберів відрізняється. Наприклад, вона має 2 * 2 == 3. Обидва вони поширюються на додаток nim, але той, хто в цьому виклику, відповідає множенню на сили 2, тоді як німбер на сірниках лише на 2 ^ (2 ^ n).
xnor

Відповіді:


36

машинний код x86: 7 байт

66 0F 3A 44 C1 00 C3  pclmulqdq xmm0, xmm1, 0 \ ret

Всього дві інструкції. pclmulqdqробить важкий підйом, він буквально реалізує цей тип xor-множення. retзробити його функцією дзвінка, сподіваючись задовольнити вимогу "вивести" результат (у зворотному значенні, xmm0). Вкладати цілі аргументи в xmmаргументи трохи незвично, але я сподіваюся, що ви пробачте мене.


1
Використання вбудованої операції звучить як обман ...
CJ Dennis

4
@CJDennis У мета-повідомленні про стандартні лазівки немає єдиної думки щодо того, забороняти її чи ні. Є 44 голоси за заборону, 31 голос проти.
isaacg

1
@isaacg Я насправді не намагаюся бути вибагливим, але формулюємо питання: Ваша мета полягає в тому, щоб реалізувати операцію множення XOR (непереносимого) . Чи відповідає ця відповідь самою операцією чи просто викликає чужу функцію? Всі інші відповіді виконують важку роботу самостійно, часто в межах кількох байтів цієї відповіді. Я думаю, що вони всі набагато розумніші і заслуговують на те, щоб заробити більше, ніж цей.
CJ Dennis

8
Я не відчуваю можливості звинувачувати відповідь, якщо питання настільки тривіальне, що воно реалізується безпосередньо загальним процесором, навряд чи можна отримати якийсь нижчий рівень, ніж цей. Це не особливо цікаво чи запам’ятовується, але здається вагомою відповіддю, тому +1.
Vality

9
У мене немає проблем із вбудованим вживанням, яке використовується для вирішення цього питання - інакше я б не знав, що така вбудована версія існує.
xnor

14

Z80, 11 байт

B7 CB 32 30 01 B3 C8 CB 23 18 F6   

Код називається функцією. aі bзнаходяться в Dі E(порядок не має значення), і відповідь зберігається, Aколи код повертається (функцій вводу / виводу немає).

B7      XOR A     //  A^=A (A=0)
CB 32   SRL D     //    CARRY = lsb(D), D>>=1, ZERO = D==0
30 01   JR NC, 1  //    jump 1 byte if not CARRY
B3      XOR E     //      A^=E, ZERO = A==0
C8      RET Z     //    return if ZERO
CB 23   SLA E     //    E<<=1
18 F6   JR -10    //    jump -10 bytes

Він дає правильні результати для всіх тестових входів, крім 63@63яких повертається, 85оскільки всі регістри є 8-бітними та 1365 мод 256 = 85 (ціле число переповнення).


10

C, 44 38 байт

Завдяки nimi, ми використовуємо рекурсію на 6 менших байтів!

f(a,b){return b?(b&1)*a^f(a*2,b/2):0;}

Визначимо функцію , fяка приймає a, b.

Це можна назвати так:

printf("%d @ %d = %d\n", 13, 14, f(13, 14));

Які виходи:

13 @ 14 = 70

Спробуйте тестові приклади в Інтернеті !


1
Чому б не рекурсивна версія f(a,b)={return(b)?(b&1)*a^f(2*a,b/2):0;}?
німі

@nimi Ах, розумний! Я знав, що існує спосіб позбутися цього німого параметра. Зараз у мене 38 байт. Спасибі!
BrainSteel

1
Викреслений 44 все ще регулярний 44. :(
Олексій А.

Входи невід'ємні , так що ви можете замінити (b&1)з , b%2щоб зберегти ще два байта , так як %мають однаковий рівень пріоритету зліва направо , як *.
CL

9

Pyth, 13 12 байт

uxyG*HQjvz2Z

Демонстрація.

uxyG*HQjvz2Z
                  Implicit:
                  z = input()
                  Q = eval(input())
                  Z = 0

       jvz2       The first input, written in base 2, like so: [1, 0, 1, ...
u      jvz2Z      Reduce over the binary representation, starting with 0.
 x                XOR of
  yG              Twice the previous number
    *HQ           and the second input times the current bit.

Стара версія, 13 байт:

xFm*vz.&Q^2dQ

Демонстрація.


Я думаю, тоді не існує хорошого способу уникнути vzприйому двох цілих входів.
xnor

@xnor Ні, на жаль.
isaacg

8

CJam, 14 13 байт

q~2bf*{\2*^}*

Як це працює :

Ми спочатку отримуємо довгі результати множення, а потім працюємо вгору, починаючи з двох нижніх пар.

q~                e# Eval the input. This puts the two numbers on stack
  2b              e# Convert the second number to binary
    f*            e# Multiply each bit of second number with the first number
                  e# This leaves an array with the candidates to be added in the long
                  e# multiplication step
      {    }*     e# Reduce on these candidates. Starting from the bottom
       \2*        e# Bit shift the lower candidate
          ^       e# XOR each other and continue

Спробуйте його онлайн тут


7

J, 14 байт

*/(~://.@)&.#:

Використання:

   5 (*/(~://.@)&.#:) 17     NB. enclosing brackets are optional
85

Пояснення (читання в основному справа наліво, uі vстояти для довільних функцій):

  • u&.#:застосовується uдо векторів двійкових уявлень вхідних чисел, а потім повернемо результат на ціле число ( u&.v == v_inverse(u(v(input_1), v(input_2))))
  • */продукти ( *) входів у продукт Декарта ( /) двох бінарних векторів
  • v(u@)застосувати uдо v(до продукту Декарта)
  • u/.застосувати uдо кожної антидіагоналі продукту Декарта (антидіагоналі представляють першу, другу, ... цифри у двійковому зображенні)
  • ~:/зменшити ( /) антидіагональну операцію XOR ( ~:)
  • Останній крок - це генерування цілого числа з бінарного вектора, про який береться перша точка.

Спробуйте його онлайн тут.


5

Python 2, 35 байт

f=lambda m,n:n and n%2*m^f(2*m,n/2)

Телефонуйте як f(13, 14). Я думаю, що більшість мов із подібною конструкцією зійдуться на щось подібне.


4

Ява, 62

(x,y)->{int r=0,i=0;for(;i<32;)r^=x*((y>>i)%2)<<i++;return r;}

Розширено

class XORMultiplication {
    public static void main(String[] args) {
        IntBinaryOperator f = (x, y) -> {
                    int r = 0, i = 0;
                    for (; i < 32;) {
                        r ^= x * ((y >> i) % 2) << i++;
                    }
                    return r;
                };
        System.out.println(f.applyAsInt(14, 13));
    }
}

1
Чи є причина , ви віддаєте перевагу , for(;i<32;)щоб while(i<32)? Вони однакової довжини, але другий здається більш природним способом її написання.
JohnE

1
@JohnE Я би здогадувався, що i++він спочатку знаходився в forциклі і отримав гольф до теперішнього положення. Оскільки whileце не менше, немає причин змінювати його.
CJ Dennis

3

Haskell, 50 байт

import Data.Bits
_#0=0
a#b=b.&.1*a`xor`2*a#div b 2

Переклад відповіді @ BrainSteel на C. Приклад використання:

map (uncurry (#)) [(0,1),(1,2),(9,0),(6,1),(3,3),(2,5),(7,9),(13,11),(5,17),(14,13),(19,1),(63,63)]
[0,2,0,6,5,10,63,127,85,70,19,1365]

3

Perl - 35 байт

#!perl -p
$\^=$`>>$_&1&&$'<<$_ for-/ /..31}{

Підрахунок параметра командного рядка як один. Введення взято з STDINмісця, розділеного простору.

Використання зразка:

$ echo 13 11 | perl xormul.pl
127
$ echo 5 17 | perl xormul.pl
85
$ echo 14 13 | perl xormul.pl
70
$ echo 19 1 | perl xormul.pl
19
$ echo 63 63 | perl xormul.pl
1365

3

Джулія, 35 33 30 байт

f(a,b)=b%2*a$(b>0&&f(2a,b÷2))

Це створює рекурсивну функцію, fяка приймає два цілих числа і повертає добуток XOR на входах.

Безголівки:

function f(a, b)
    # Bitwise XOR : $
    # Short-circuit AND : &&

    b % 2 * a $ (b > 0 && f(2a, b ÷ 2))
end

Збережено пару байтів із заохоченням від Sp3000!


2

Пітон 2, 104 91 78 66 байт

def y(a,b,c=0):
 for _ in bin(b)[:1:-1]:c^=int(_)*a;a<<=1
 print c

Візьміть біти bу зворотному порядку, закінчуючи перед тим, як натиснути '0b'на початок рядка. Помножте кожен на aі xorна загальний, а потім зсув вліво a. Потім надрукуйте загальну суму.



2

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

1

Рубі, 76 75 73 байт

a,b=$*.map{|x|x.to_i}
o=0
while(b>0)
o^=a&-(b&1)
a<<=1
b>>=1
end
puts(o)

Ruby, 60 байт (лише функція, без вводу / виводу)

def t(a,b)
o=0
while(b>0)
o^=a&-(b&1)
a<<=1
b>>=1
end
t
end


1

Дартс, 34 32 байти

m(a,b)=>a<1?0:a%2*b^m(a~/2,b*2);

Пряма рекурсивна реалізація.



1

GNU Assembler (x86_64 Mac OS X), 97 байт

Це правильна функція, яку можна викликати з C:

.text
.globl _f
_f:
movq %rdi,%xmm0;movq %rsi,%xmm1;pclmulqdq $0,%xmm1,%xmm0;movq %xmm0,%rax;ret

& можна перевірити за допомогою цієї програми:

#include <stdio.h>
int f(int a, int b);
#define p(a,b) printf("%d %d %d\n", a, b, f(a, b))
int main(void)
{
    p(0,1);
    p(1,2);
    p(9,0);
    p(6,1);
    p(3,3);
    p(2,5);
    p(7,9);
    p(13,11);
    p(5,17);
    p(14,13);
    p(19,1);
    p(63,63);
}

Зауважте, що на Mac OS X ви повинні використовувати clang -x c для компіляції це як C &, а не C ++.

Для Linux (якщо я пам’ятаю правильно) код буде 95 байт:

.text
.globl f
f:
movq %rdi,%xmm0;movq %rsi,%xmm1;pclmulqdq $0,%xmm1,%xmm0;movq %xmm0,%rax;ret

Як не дивно, ця версія насправді довша, ніж визначення функції в вбудованій збірці, але ця була довшою, ніж чисте рішення C, яке ми вже маємо, тому я вирішила спробувати збірку.

редагувати

Якщо він рахується за зібраним розміром (без урахування будь-яких міток і с.), То це

x86_64 Асемблер, 22 байти:

0:  66 48 0f 6e c7          movq         %rdi,  %xmm0
5:  66 48 0f 6e ce          movq         %rsi,  %xmm1
a:  66 0f 3a 44 c1 00       pclmullqlqdq $0,    %xmm1,%xmm0
10: 66 48 0f 7e c0          movq         %xmm0, %rax
15: c3                      ret

Думаю, ви можете виміряти мови складання за їх складеною формою.
Нісса


0

Цейлон, 90 байт

alias I=>Integer;I x(I a,I b)=>[for(i in 0:64)if(b.get(i))a*2^i].fold(0)((y,z)=>y.xor(z));

Це лише алгоритм, як описано: помножте aна те, 2^iде встановлений цей iбіт b, і додайте їх усі разом, використовуючи xor. Iterates over 0:64тому, що Integers є 64-розрядною на Цейлоні при запуску на JVM (нижчий при запуску як Javascript, але потім b.get(i)просто повертає false).

Відформатовано:

alias I => Integer;

I x(I a, I b) =>
      [
        for (i in 0:64)
            if (b.get(i))
                a * 2^i
      ].fold(0)((y, z) => y.xor(z));

Псевдонім зберігає тут лише один байт.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.