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

(对于数学家,这是多项式环中的乘法,通过将Z F_2[x]评估x=2为多项式来标识具有自然数的多项式。)

XOR乘法通过按位XOR 交换a@b=b@a,关联(a@b)@c=a@(b@c)和分布a@(b^c)=(a@b)^(a@c)。实际上,a@b=a*b每当匹配乘法a并且b2like的幂时,就是这样独特的运算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
这就是众所周知的“无进位乘法”,您可能要添加问题标题,并且很有可能最小的条目是PCLMULQDQ来自CLMUL扩展名的6字节x86指令。不幸的是,我之前不了解x86指令集(与有关PEXT/PDEP),所以对此表示不满,因此我将在此处保留其注释。
Iwillnotexist Idonotexist

@IwillnotexistIdonotexist感谢您的来信,很高兴为Google取个名字。
xnor

如果上面的内容不是“ xor”,则必须以不同于xorc或xornc的方式调用...不是xor
RosLuP

1
@RosLuP不是异或,而是异或。
xnor

@boboquack实际上,我相信木材乘法 是不同的。例如,它具有2 * 2 == 3。这两个都是通过nim加法分布的,但是这个挑战中的一个是2的乘方匹配乘法,而nimber的匹配仅是2 ^(2 ^ n)匹配。
xnor

Answers:


36

x86机器码:7个字节

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

只有两条指令。pclmulqdq做繁重的工作,它实际上实现了那种异或乘法。ret使其成为可调用函数,希望满足“输出”结果(在返回值中xmm0)的要求。将整数参数放入xmmargs有点不寻常,但我希望您能原谅我。


1
使用内置的操作听起来像是在作弊...
CJ Dennis

4
@CJDennis在“标准漏洞”元文章中,关于是否应该禁止它尚无共识。禁止票有44票,反对票有31票。
isaacg 2015年

1
@isaacg我实际上并不是想挑剔,但问题的措辞是:您的目标是实现XOR(无位)乘法的运算。这个答案本身是“实现”操作本身还是只是调用其他人的功能?所有其他答案本身都会进行艰苦的工作,通常在此答案的几个字节之内。我认为他们都非常聪明,应该比这更值得赞扬。
CJ丹尼斯

8
如果问题是如此琐碎,以至于它是由一个普通的CPU直接实现的,那么我真的不觉得应该怪问题的答案,几乎没有人能比这更低的水平了。它不是特别有趣或令人难忘,但确实是一个有效答案,因此+1。

9
我对使用内置程序来解决此问题没有任何疑问-否则,我将不知道存在这样的内置程序。
xnor 2015年

14

Z80,11个字节

B7 CB 32 30 01 B3 C8 CB 23 18 F6   

该代码称为函数。a并且bD和中E(顺序无关紧要),并且答案A在代码返回时存储在其中(没有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

对于所有测试输入63@63,它将产生正确的结果,但返回的结果除外,85因为所有寄存器均为8位且1365 mod 256 = 85(整数溢出)。


10

C,44 38字节

多亏了nimi,我们现在使用递归少了6个字节!

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

我们定义一个函数f这需要ab

可以这样称呼:

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

@nimi啊,聪明!我知道有一种方法可以摆脱那个愚蠢的参数。我现在有38个字节。谢谢!
BrainSteel 2015年

1
被淘汰的44仍然是常规44。:(
Alex A.

输入为非负数,因此您可以替换(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 2015年

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@)适用uv(笛卡尔产品)
  • u/. 应用 u于笛卡尔乘积的每个反对角线(反对角线代表二进制表示形式的第1、2,...位数字)
  • ~://用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丹尼斯2015年

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

Python 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。注意,在GAP 中GF(2)为F 2

R:=PolynomialRing(GF(2));

接下来,我们将GAP变量分配给xring的不确定对象R。现在,无论我x在GAP中说什么,系统都会知道我在说环的不确定性R

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

接下来,我们有两个函数,它们是彼此的逆映射。这些映射都可以使用,但是它们并不保留结构,因此我无法找到在GAP中实现它们的更好方法。几乎可以肯定有更好的方法,如果您知道,请发表评论!

第一个映射to_ring取一个整数并将其映射到其相应的ring元素。它通过使用转换为二进制算法来做到这一点,其中1二进制中出现的每一个都被替换为x^nwhere 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));

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个字节(仅功能,无I / O)

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


1

Dart,34 32字节

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

直接递归实现。


1

gnuplot,29个字节

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

就像在Dart中一样(见上文)


1

GNU汇编程序(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

并可以通过以下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);
}

请注意,在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解决方案要长,所以我决定尝试汇编。

编辑

如果按组装尺寸(不包括标签&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

我认为您可以通过汇编语言来衡量汇编语言。
尼莎(Nissa)


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。进行迭代0:64是因为在JVM上运行时,Ceylon中的Integers是64位的(以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));

这里的别名保险箱只有一个字节。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.