Moltiplicazione XOR


33

L'obiettivo è implementare l'operazione di moltiplicazione XOR ( carryless ), definita di seguito, nel minor numero di byte possibile.

Se pensiamo a XOR bit a bit ( ^) come aggiunta binaria senza trasportare

   101   5
^ 1001   9
  ----  
  1100  12

  5^9=12

possiamo eseguire la moltiplicazione XOR eseguendo la moltiplicazione @binaria lunga ma facendo il passo di aggiunta senza portare come XOR bit a bit ^.

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

  14@13=70

(Per i matematici, questa è la moltiplicazione nell'anello polinomiale F_2[x], identificando i polinomi con numeri naturali valutando x=2come un polinomio su Z.)

La moltiplicazione XOR commuta a@b=b@a, associa (a@b)@c=a@(b@c)e distribuisce su XOR bit a bit a@(b^c)=(a@b)^(a@c). In effetti, è l'unica operazione del genere che corrisponde alla moltiplicazione a@b=a*bogni volta ae bsono poteri 2simili 1,2,4,8....

Requisiti

Prendi due numeri interi non negativi come input e output o stampa il loro prodotto XOR. Questo dovrebbe essere come numeri o loro rappresentazioni di stringhe decimali, non loro espansioni binarie. Vince il minor numero di byte.

Non preoccuparti di overflow di numeri interi.

Ecco alcuni casi di test formattati come 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
Questo è meglio noto come "moltiplicazione carry-less", che potresti voler aggiungere il titolo della domanda, e con alta probabilità la voce più piccola è l'istruzione x86 a 6 byte PCLMULQDQdall'estensione CLMUL. Sfortunatamente sono stato sottoposto a downgrade per la mia conoscenza dell'istruzione x86 impostata prima (relativa a PEXT/PDEP), quindi lascerò questo come commento qui.
Iwillnotexist Idonotexist,

@IwillnotexistIdonotexist Grazie per la nota, è bello avere un nome su Google.
xnor

Se quello sopra non è "xor" devi chiamare in modo diverso come xorc o xornc ... Non è xor
RosLuP

1
@RosLuP Non è xor, è xor moltiplicazione.
xnor

@boboquack In realtà, credo che la moltiplicazione di nimber sia diversa. Ad esempio, ha 2 * 2 == 3. Entrambi si distribuiscono su nim addizione, ma quello in questa sfida corrisponde alla moltiplicazione su potenze di 2, mentre il nimber su corrisponde solo su 2 ^ (2 ^ n).
xnor

Risposte:


36

codice macchina x86: 7 byte

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

Solo due istruzioni. pclmulqdqfa il sollevamento pesante, implementa letteralmente quel tipo di moltiplicazione xor. retper renderlo una funzione richiamabile, si spera che soddisfi il requisito di "produrre" il risultato (nel valore di ritorno, xmm0). Mettere argomenti interi in xmmargs è un po 'insolito, ma spero che mi perdonerai.


1
Usare un'operazione integrata suona come un imbroglio ...
CJ Dennis,

4
@CJDennis Sul meta post Standard Loopholes, non vi è consenso sul fatto che debba essere vietato o meno. Ci sono 44 voti per il divieto, 31 voti contrari.
isaacg,

1
@isaacg Non sto davvero cercando di essere pignolo, ma la formulazione della domanda è: il tuo obiettivo è implementare l'operazione di moltiplicazione XOR (carryless) . Questa risposta "implementa" l'operazione stessa o chiama semplicemente la funzione di qualcun altro? Tutte le altre risposte fanno il duro lavoro da sole, spesso entro pochi byte da questa risposta. Penso che siano tutti molto più intelligenti e meritino di votare più di questo.
CJ Dennis,

8
Non mi sento davvero in grado di incolpare una risposta se la domanda è così banale che viene implementata direttamente da una CPU comune, difficilmente si può ottenere un livello inferiore a quello. Non è particolarmente interessante o memorabile, ma sembra una risposta valida, quindi +1.
Vality,

9
Non ho alcun problema con un built-in utilizzato per risolvere questo problema, altrimenti non avrei saputo che esiste un tale built-in.
xnor

14

Z80, 11 byte

B7 CB 32 30 01 B3 C8 CB 23 18 F6   

Il codice viene chiamato come funzione. ae bsono in De E(l'ordine non ha importanza) e la risposta viene memorizzata Aquando il codice ritorna (non ci sono funzioni I / O).

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

Produce i risultati corretti per tutti gli input di test, tranne quelli 63@63che restituiscono 85perché tutti i registri sono 8 bit e 1365 mod 256 = 85 (overflow intero).


10

C, 44 38 byte

Grazie a nimi, ora utilizziamo la ricorsione per 6 byte in meno!

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

Si definisce una funzione fche prende a, b.

Questo può essere chiamato come:

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

Quali uscite:

13 @ 14 = 70

Prova i casi di test online !


1
Perché non una versione ricorsiva f(a,b)={return(b)?(b&1)*a^f(2*a,b/2):0;}?
nimi,

@nimi Ah, intelligente! Sapevo che c'era un modo per sbarazzarsi di quel parametro stupido. Adesso ho 38 byte. Grazie!
BrainSteel,

1
Colpito 44 è ancora regolare 44. :(
Alex A.

Gli input non sono negativi, quindi è possibile sostituirli (b&1)con b%2per salvare altri due byte poiché %hanno lo stesso livello di precedenza da sinistra a destra *.
CL-

9

Pyth, 13 12 byte

uxyG*HQjvz2Z

Dimostrazione.

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.

Vecchia versione, 13 byte:

xFm*vz.&Q^2dQ

Dimostrazione.


Immagino quindi che non ci sia un buon modo per evitare vzdi prendere due input interi.
xnor

@xnor No, sfortunatamente.
isaacg,

8

CJam, 14 13 byte

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

Come funziona :

Otteniamo prima i risultati della moltiplicazione lunga e poi procediamo verso l'alto partendo dalle due coppie inferiori.

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

Provalo online qui


7

J, 14 byte

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

Uso:

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

Spiegazione (leggendo principalmente da destra a sinistra; ue vsta per funzioni arbitrarie):

  • u&.#:si applica uai vettori delle rappresentazioni binarie dei numeri di input, quindi riporta il risultato a un numero intero ( u&.v == v_inverse(u(v(input_1), v(input_2))))
  • */prodotti ( *) di input nel prodotto Descartes ( /) del vettore binario due
  • v(u@)applica ua v(al prodotto Descartes)
  • u/.si applicano uad ogni anti-diagonale del prodotto Descartes (gli anti-diagonali rappresentano la 1a, 2a, ... cifra nella rappresentazione binaria)
  • ~:/ridurre ( /) un anti-diagonale con operazione XOR ( ~:)
  • L'ultimo passo è generare un numero intero dal vettore binario di cui si occupa il primo punto.

Provalo online qui.


5

Python 2, 35 byte

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

Chiama come f(13, 14). Penso che la maggior parte delle lingue con un costrutto simile convergeranno su qualcosa del genere.


4

Java, 62

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

allargato

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
C'è una ragione si preferisce for(;i<32;)a while(i<32)? Hanno la stessa lunghezza, ma il secondo sembra un modo più naturale di scriverlo.
Giovanni,

1
@JohnE Immagino che i++fosse originariamente nel forcircuito e sia stato portato nella posizione attuale. Dal momento che whilenon è più piccolo non c'è motivo di cambiarlo.
CJ Dennis,

3

Haskell, 50 byte

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

Una traduzione della risposta C di @ BrainSteel. Esempio di utilizzo:

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 byte

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

Contando l'opzione della riga di comando come una. L'input è preso da STDIN, spazio separato.

Esempio di utilizzo:

$ 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

Julia, 35 33 30 byte

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

Questo crea una funzione ricorsiva fche accetta due numeri interi e restituisce il prodotto XOR degli input.

Ungolfed:

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

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

Ho salvato un paio di byte con l'incoraggiamento di Sp3000!


2

Python 2, 104 91 78 66 byte

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

Prendi i bit di bin ordine inverso, finendo prima di colpire '0b'all'inizio della stringa. Moltiplicare ciascuno per ae xorper il totale, quindi spostare a sinistra a. Quindi stampare il totale.



2

GAP , 368 byte

Per i matematici, questa è la moltiplicazione nell'anello polinomiale F_2 [x], identificando i polinomi con numeri naturali valutando a x = 2 come un polinomio su Z.

Certo, facciamolo! (questo è solo vagamente giocato a golf, il punto era più di spostarsi in F 2 [x] e fare i calcoli più di qualsiasi tentativo di essere una voce vincente)

Ecco il codice

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;

Ecco il codice non golfato con spiegazione:

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;

Ok, quindi prima di tutto, creiamo l'anello polinomiale univariato sul campo F 2 e lo chiamiamo R. Si noti che GF(2)è F 2 in GAP.

R:=PolynomialRing(GF(2));

Successivamente, assegneremo la variabile GAP xall'indeterminato dell'anello R. Ora, ogni volta che dico xin GAP, il sistema saprà che sto parlando dell'indeterminato dell'anello R.

x:=IndeterminatesOfPolynomialRing(R);
x:=x[1];

Successivamente, abbiamo due funzioni, che sono mappe inverse l'una dell'altra. Queste mappe sono entrambe su, ma non preservano la struttura, quindi non sono riuscito a trovare un modo migliore per implementarle in GAP. C'è quasi sicuramente un modo migliore, se lo conosci, per favore commenta!

La prima mappa, to_ringprende un numero intero e lo mappa al suo elemento ad anello corrispondente. Lo fa usando una conversione in algoritmo binario, dove ogni cosa 1che appare in binario viene sostituita da una x^ndove nè la potenza appropriata che 2 prenderebbe se il numero fosse effettivamente binario.

    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;

La prossima funzione inverte questo. to_intsprende un elemento ring e lo mappa al suo intero corrispondente. Lo faccio ottenendo un elenco dei coefficienti del polinomio e per ogni coefficiente diverso da zero, il risultato viene aumentato di 2 ^ n, nello stesso modo in cui convertiremmo binario in decimale.

    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;

Per il passaggio finale, chiamiamo queste funzioni. Prendiamo i due input interi, li convertiamo in elementi nell'anello R, quindi moltiplichiamo questi elementi insieme e rimandiamo il prodotto agli interi.

return to_ints( to_ring(i)*to_ring(j));

1

Rubino, 76 75 73 byte

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 byte (solo funzione, no I / O)

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


1

Dardo, 34 32 byte

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

Implementazione ricorsiva diretta.


1

gnuplot, 29 byte

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

proprio come in Dart (vedi sopra)


1

Assemblatore GNU (x86_64 Mac OS X), 97 byte

Questa è una funzione appropriata che può essere chiamata da C:

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

e può essere testato con questo programma C:

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

Si noti che su Mac OS X, è necessario utilizzare clang -x cper compilarlo come C e non C ++.

Per Linux (se ricordo bene), il codice sarebbe 95 byte:

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

Stranamente, questa versione è in realtà più lunga della definizione della funzione nell'assemblaggio in linea, ma quella era più lunga della soluzione C pura che già abbiamo, quindi ho deciso di provare l'assemblaggio.

modificare

Se viene conteggiato dalla dimensione assemblata (escluse eventuali etichette e altro), lo è

Assemblatore x86_64, 22 byte:

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

Penso che dovresti misurare i linguaggi di assemblaggio tramite il loro modulo compilato.
Nissa,

0

golflua 68

x,y=I.r("*n","*n")r=0~@i=0,31r=B.x(r,x*B.ls(B.rs(y,i)%2,i+1))$w(r/2)

Fondamentalmente fa lo stesso bit- shift della risposta Java di Ypnypn , ma sembra che la divisione per 2 alla fine funzioni correttamente. Comprende valori come stdin, esempi di seguito

> 14 13 
70
> 19 1 
19
> 5 17 
85

0

Ceylon, 90 byte

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

Questo è solo l'algoritmo come descritto: moltiplicare aper 2^iil ipunto in cui è impostato il bit be aggiungerli tutti insieme usando xor. Scorre 0:64perché gli Integer sono a 64 bit in Ceylon quando si esegue su JVM (inferiore quando si esegue come Javascript, ma b.get(i)restituisce semplicemente false).

formattato:

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

L'alias salva qui solo un singolo byte.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.