Phép nhân XOR


33

Mục tiêu của bạn là triển khai hoạt động của phép nhân XOR (không mang theo ), được xác định bên dưới, với càng ít byte càng tốt.

Nếu chúng ta nghĩ về bitwise XOR ( ^) là phép cộng nhị phân mà không mang theo

   101   5
^ 1001   9
  ----  
  1100  12

  5^9=12

chúng ta có thể thực hiện phép nhân XOR @bằng cách thực hiện phép nhân dài nhị phân nhưng thực hiện bước thêm mà không mang theo XOR bitwise ^.

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

  14@13=70

(Đối với các nhà toán học, đây là phép nhân trong vòng đa thức F_2[x], xác định đa thức với số tự nhiên bằng cách đánh giá tạix=2 là đa thức trên Z.)

Phép nhân XOR đi lại a@b=b@a, liên kết (a@b)@c=a@(b@c)và phân phối trên XOR bitwise a@(b^c)=(a@b)^(a@c). Trong thực tế, nó là hoạt động độc đáo như vậy phù hợp với phép nhân a@b=a*bbất cứ khi nào ablà sức mạnh của 2thích 1,2,4,8....

Yêu cầu

Lấy hai số nguyên không âm làm đầu vào và đầu ra hoặc in sản phẩm XOR của chúng. Đây phải là số hoặc biểu diễn chuỗi thập phân của chúng, không phải là mở rộng nhị phân của chúng. Ít byte nhất sẽ thắng.

Đừng lo lắng về số nguyên tràn.

Dưới đây là một số trường hợp thử nghiệm được định dạng là 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
Điều này được biết đến nhiều hơn là "phép nhân không mang theo", mà bạn có thể muốn thêm tiêu đề câu hỏi và với xác suất cao, mục nhập nhỏ nhất là lệnh x86 6 byte PCLMULQDQtừ phần mở rộng CLMUL. Thật không may, tôi đã bị đánh giá thấp về kiến ​​thức của mình về tập lệnh x86 trước đó (Liên quan đến PEXT/PDEP), vì vậy tôi sẽ chỉ để lại nhận xét này ở đây.
Idillotexist Idillotexist

@IwillnotexistIdonotexist Cảm ơn bạn đã lưu ý, thật tuyệt khi có tên cho Google.
xnor

Nếu ở trên không phải là "xor", bạn phải gọi theo một cách khác là xorc hoặc xornc ... Đó không phải là xor
RosLuP

1
@RosLuP Không phải là xor, đó là phép nhân xor.
xnor

@boboquack Thật ra, tôi tin rằng nhân nimber là khác nhau. Chẳng hạn, nó có 2 * 2 == 3. Cả hai đều phân phối trên nim bổ sung, nhưng một trong thử thách này phù hợp với phép nhân với sức mạnh của 2, trong khi nimber trên các trận đấu chỉ trên 2 ^ (2 ^ n).
xnor

Câu trả lời:


36

Mã máy x86: 7 byte

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

Chỉ có hai hướng dẫn. pclmulqdqthực hiện việc nâng vật nặng, theo nghĩa đen nó thực hiện kiểu nhân xor đó. retđể làm cho nó trở thành một hàm có thể gọi được, hy vọng đáp ứng yêu cầu "xuất" kết quả (trong giá trị trả về, xmm0). Đặt các đối số nguyên trong các đối số xmmlà một chút khác thường, nhưng tôi hy vọng bạn sẽ tha thứ cho tôi.


1
Sử dụng một hoạt động tích hợp nghe có vẻ như gian lận ...
CJ Dennis

4
@CJDennis Trên bài đăng meta Lỗ hổng tiêu chuẩn, không có sự đồng thuận nào về việc có nên cấm hay không. Có 44 phiếu cấm, 31 phiếu chống.
isaacg

1
@isaacg Tôi thực sự không cố gắng tỏ ra kén chọn nhưng từ ngữ của câu hỏi là: Mục tiêu của bạn là triển khai hoạt động của phép nhân XOR (không mang theo) . Liệu câu trả lời này "thực hiện" chính hoạt động đó hay chỉ đơn giản là gọi chức năng của người khác? Tất cả các câu trả lời khác đều tự làm việc chăm chỉ, thường trong một vài byte của câu trả lời này. Tôi nghĩ rằng tất cả họ đều thông minh hơn rất nhiều và xứng đáng được nâng cao hơn thế này.
CJ Dennis

8
Tôi thực sự không cảm thấy có thể đổ lỗi cho một câu trả lời nếu câu hỏi quá tầm thường, nó được thực hiện trực tiếp bởi một CPU thông thường, người ta khó có thể đạt được mức nào thấp hơn mức đó. Nó không đặc biệt thú vị hoặc đáng nhớ nhưng dường như là một câu trả lời hợp lệ, vì vậy +1.
Vality

9
Tôi không có vấn đề gì với việc tích hợp sẵn được sử dụng để giải quyết vấn đề này - nếu không, tôi sẽ không biết có tồn tại tích hợp như vậy.
xnor

14

Z80, 11 byte

B7 CB 32 30 01 B3 C8 CB 23 18 F6   

Mã được gọi là một hàm. abnằm trong DE(thứ tự không thành vấn đề) và câu trả lời được lưu trữ Akhi mã trả về (không có chức năng 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

Nó tạo ra kết quả chính xác cho tất cả các đầu vào kiểm tra ngoại trừ 63@63trả về 85vì tất cả các thanh ghi là 8 bit và 1365 mod 256 = 85 (tràn số nguyên).


10

C, 44 38 byte

Nhờ nimi, giờ đây chúng tôi sử dụng đệ quy cho 6 byte ít hơn!

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

Chúng tôi xác định một chức năng f mà mất a,b .

Điều này có thể được gọi là như:

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

Đầu ra nào:

13 @ 14 = 70

Hãy thử các trường hợp thử nghiệm trực tuyến !


1
Tại sao không phải là một phiên bản đệ quy f(a,b)={return(b)?(b&1)*a^f(2*a,b/2):0;}?
nimi

@nimi À, thông minh! Tôi biết có một cách để thoát khỏi tham số ngu ngốc đó. Tôi đã có 38 byte bây giờ. Cảm ơn!
BrainSteel

1
Struck out 44 vẫn bình thường 44. :(
Alex A.

Các đầu vào không âm, do đó bạn có thể thay thế (b&1)bằng b%2để lưu thêm hai byte vì %có cùng mức ưu tiên từ trái sang phải như *.
CL-

9

Bình thường, 13 12 byte

uxyG*HQjvz2Z

Trình diễn.

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.

Phiên bản cũ, 13 byte:

xFm*vz.&Q^2dQ

Trình diễn.


Tôi đoán sau đó không có cách nào tốt để tránh vzlấy hai số nguyên.
xnor

@xnor Không, không may.
isaacg

8

CJam, 14 13 byte

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

Cách thức hoạt động :

Đầu tiên chúng tôi nhận được kết quả nhân dài và sau đó làm việc theo cách của chúng tôi bắt đầu từ hai cặp dưới cùng.

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

Dùng thử trực tuyến tại đây


7

J, 14 byte

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

Sử dụng:

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

Giải thích (đọc chủ yếu từ phải sang trái; uvà đại diện vcho các chức năng tùy ý):

  • u&.#:áp dụng ucho các vectơ của biểu diễn nhị phân của các số đầu vào, sau đó biến kết quả trở lại thành một số nguyên ( u&.v == v_inverse(u(v(input_1), v(input_2))))
  • */sản phẩm ( *) của đầu vào trong sản phẩm Descartes ( /) của hai vectơ nhị phân
  • v(u@)áp dụng ucho v(đối với sản phẩm Descartes)
  • u/.áp dụng ucho mọi đường chéo của sản phẩm Descartes (chống đường chéo đại diện cho các chữ số 1, 2, ... trong biểu diễn nhị phân)
  • ~:/giảm ( /) một đường chéo với hoạt động XOR (~: )
  • Bước cuối cùng là tạo một số nguyên từ vectơ nhị phân mà điểm đầu tiên quan tâm.

Hãy thử trực tuyến tại đây.


5

Python 2, 35 byte

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

Gọi như thế f(13, 14). Tôi nghĩ rằng hầu hết các ngôn ngữ có cấu trúc tương tự sẽ hội tụ một thứ như thế này.


4

Java, 62

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

Mở rộng

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ó một lý do bạn thích for(;i<32;)để while(i<32)? Chúng có cùng độ dài, nhưng cách thứ hai có vẻ như là một cách tự nhiên hơn để viết nó.
JohnE

1
@ John Tôi đoán rằng i++ban đầu trong forvòng lặp và đã chơi golf đến vị trí hiện tại của nó. Vì whilekhông nhỏ hơn nên không có lý do để thay đổi nó.
CJ Dennis

3

Haskell, 50 byte

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

Bản dịch câu trả lời C của @ BrainSteel. Ví dụ sử dụng:

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}{

Đếm tùy chọn dòng lệnh là một. Đầu vào được lấy từSTDIN , không gian tách biệt.

Sử dụng mẫu:

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

Điều này tạo ra một hàm đệ quy f có hai số nguyên và trả về sản phẩm XOR của các đầu vào.

Ung dung:

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

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

Đã lưu một vài byte với sự khích lệ từ 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

Lấy các bit btheo thứ tự ngược lại, kết thúc trước khi bạn nhấn '0b'vào đầu chuỗi. Nhân mỗi số một axorvới tổng, sau đó dịch chuyển trái a. Sau đó in tổng số.



2

GAP , 368 byte

Đối với các nhà toán học, đây là phép nhân trong vòng đa thức F_2 [x], xác định đa thức với số tự nhiên bằng cách đánh giá tại x = 2 là đa thức trên Z.

Chắc chắn, hãy làm điều đó! (đây chỉ là môn đánh gôn lỏng lẻo, điểm quan trọng hơn là di chuyển vào F 2 [x] và thực hiện các phép tính nhiều hơn bất kỳ nỗ lực nào để trở thành một mục thắng)

Đây là mã

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;

Đây là mã không được giải thích với lời giải thích:

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;

Được rồi, trước hết, chúng ta tạo vòng đa thức đơn biến trên trường F 2 và gọi nó R. Lưu ý rằng đó GF(2)là F 2 trong GAP.

R:=PolynomialRing(GF(2));

Tiếp theo, chúng ta sẽ gán biến GAP xcho phần không xác định của vòng R. Bây giờ, bất cứ khi nào tôi nói xtrong GAP, hệ thống sẽ biết tôi đang nói về sự không xác định của chiếc nhẫn R.

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

Tiếp theo, chúng ta có hai chức năng, đó là các bản đồ nghịch đảo của nhau. Các bản đồ này đều nằm trên, nhưng chúng không bảo tồn cấu trúc, vì vậy tôi không thể tìm ra cách nào tốt hơn để thực hiện chúng trong GAP. Gần như chắc chắn là một cách tốt hơn, nếu bạn biết điều đó, xin vui lòng bình luận!

Bản đồ đầu tiên, to_ringlấy một số nguyên và ánh xạ tới phần tử vòng tương ứng của nó. Nó làm điều này bằng cách sử dụng một chuyển đổi thuật toán nhị phân, nơi mỗi 1đó sẽ xuất hiện trong hệ nhị phân được thay thế bằng một x^nnơi nlà sức mạnh thích hợp mà 2 sẽ thực hiện nếu con số này là thực sự nhị phân.

    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;

Các chức năng tiếp theo đảo ngược điều này. to_intslấy một phần tử vòng và ánh xạ nó tới số nguyên tương ứng của nó. Tôi làm điều này bằng cách lấy danh sách các hệ số của đa thức và với mỗi hệ số khác 0, kết quả được tăng thêm 2 ^ n, giống như cách chúng ta sẽ chuyển đổi nhị phân thành thập phâ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;

Đối với bước cuối cùng, chúng tôi gọi các chức năng này. Chúng tôi lấy hai đầu vào số nguyên, chuyển đổi chúng thành các phần tử trong vòng R, sau đó nhân các phần tử này lại với nhau và gửi sản phẩm trở lại số nguyên.

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

1

Ruby, 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 (chỉ chức năng, không có I / O)

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


1

Phi tiêu, 34 32 byte

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

Thực hiện đệ quy thẳng về phía trước.


1

gnuplot, 29 byte

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

giống như trong Dart (xem ở trên)


1

Trình biên dịch GNU (x86_64 Mac OS X), 97 byte

Đây là một chức năng phù hợp có thể được gọi từ C:

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

& có thể được thử nghiệm với chương trình C này:

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

Lưu ý rằng trên Mac OS X, bạn phải sử dụng clang -x c để biên dịch nó dưới dạng C & không phải C ++.

Đối với linux (nếu tôi nhớ đúng), mã sẽ là 95 byte:

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

Thật kỳ lạ, phiên bản này thực sự dài hơn việc xác định chức năng trong lắp ráp nội tuyến, nhưng phiên bản đó dài hơn giải pháp C thuần túy mà chúng tôi đã có, vì vậy tôi quyết định thử lắp ráp.

chỉnh sửa

Nếu nó được tính theo kích thước lắp ráp (không bao gồm bất kỳ nhãn & c.), Thì đó là

Trình biên dịch 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

Tôi nghĩ rằng bạn đo ngôn ngữ lắp ráp bằng hình thức biên dịch của họ mặc dù.
Nissa

0

sân golf 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)

Về cơ bản, các bithifting giống như câu trả lời Java của Ypnypn , nhưng dường như yêu cầu chia cho 2 ở cuối để hoạt động chính xác. Lấy các giá trị như stdin, ví dụ dưới đây

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

Đây chỉ là thuật toán như được mô tả: nhân avới 2^ibất cứ nơi nào ibit thứ được đặt vào bvà cộng tất cả chúng lại với nhau bằng xor. Lặp đi lặp lại 0:64vì Số nguyên là 64-bit trong Ceylon khi chạy trên JVM (thấp hơn khi chạy dưới dạng Javascript, nhưng sau đób.get(i) chỉ trả về sai).

Định dạng:

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

Các bí danh ở đây chỉ là một byte.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.