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=2Z上の多項式としてat を評価することにより自然数で多項式を識別します。)

XOR乗算はa@b=b@a(a@b)@c=a@(b@c)ビット単位のXORで交換、関連付け、および分散しa@(b^c)=(a@b)^(a@c)ます。実際には、乗算と一致したユニークな操作でa@b=a*bいつでもabの力ある2ように1,2,4,8...

必要条件

入力および出力として2つの非負整数を取るか、XOR積を出力します。これは、バイナリ展開ではなく、数値または10進数の文字列表現でなければなりません。最少バイトが勝ちます。

整数オーバーフローについて心配する必要はありません。

以下にフォーマットされたいくつかのテストケースを示し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命令です。残念ながら、以前(xに関連するPEXT/PDEP)x86命令セットの知識に落胆したため、ここにコメントとして残しておきます。
Iwillnotexist Idonotexist

@IwillnotexistIdonotexistメモをありがとう、Googleに名前を付けてよかった。
xnor

なお、上記でない場合は、「XOR」あなたはXORCやxorncなど、さまざまな方法で呼び出す必要があります...それはXORではありません
RosLuP

1
@RosLuP xorではなく、xor乗算です。
XNOR

@boboquack実際、私は木材の掛け算 が違うと信じています。たとえば、2 * 2 == 3です。これらは両方ともnimの加算を介して分配されますが、このチャレンジの1つは2の累乗の乗算に一致しますが、上の木材は2 ^(2 ^ n)にのみ一致します。
XNOR

回答:


36

x86マシンコード:7バイト

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

指示は2つだけです。pclmulqdq重い持ち上げを行い、文字通りそのタイプのxor乗算を実装します。ret呼び出し可能な関数にするために、結果(戻り値でxmm0)を「出力」するという要件を満たせばよいのです。xmmargsに整数の引数を入れることは少し珍しいですが、あなたが私を許してくれることを願っています。


1
...不正行為のような動作音に建て使用して
CJデニス

4
@CJDennis Standard Loopholesメタポストでは、禁止すべきかどうかについてのコンセンサスはありません。禁止は44票、反対は31票です。
isaacg

1
@isaacg私は本当に気難しいことをしようとはしていませんが、質問の文言は次のとおりです。あなたの目標はXOR(キャリーレス)乗算の演算を実装することです。これは、操作自体を「実装」するか、単に他の人の機能を呼び出すだけですか?他のすべての回答は、多くの場合、この回答の数バイト以内で、ハードワーク自体を行います。私は彼らは皆とても賢く、これ以上の賛成に値すると思います。
CJデニス

8
質問が一般的なCPUによって直接実装されているため、それよりも低いレベルを取得することはほとんどできない場合、答えを責めることはできません。それは特に面白くも記憶に残るものでもありませんが、有効な答えのように思えますので、+ 1。
バリティ

9
私はこれを解決するために使用されているビルトインに問題はありません-そうでなければ、そのようなビルトインが存在することを知りません。
xnor

14

Z80、11バイト

B7 CB 32 30 01 B3 C8 CB 23 18 F6   

コードは関数として呼び出されます。aand bDand 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

すべてのレジスタが8ビットで1365 mod 256 = 85(整数オーバーフロー)であるため、63@63どの入力が返される場合を除き、すべてのテスト入力に対して正しい結果が生成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ああ、賢い!その愚かなパラメーターを取り除く方法があることは知っていました。38バイトになりました。ありがとう!
BrainSteel

1
ストライクアウト44はまだ通常の44です。:(
アレックスA.

入力は負ではないため、と同じ左から右への優先順位レベルを(b&1)持っb%2ているため、さらに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

デモンストレーション。


私はそれからvz2つの整数入力を取ることを避ける良い方法はないと思います。
xnor

@xnorいいえ、残念ながら。
isaacg

8

CJam、14 13バイト

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

仕組み

最初に長い乗算結果を取得してから、下の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

説明(主に右から左へ読みます; 任意の機能uv表します):

  • u&.#:u入力数値のバイナリ表現のベクトルに適用し、結果を整数に戻します(u&.v == v_inverse(u(v(input_1), v(input_2)))
  • */2つのバイナリベクトルの*デカルト積()の入力の積(/
  • v(u@)適用uするv(デカルトの製品に)
  • u/.uデカルト積のすべての対角に適用します(対角は2進表現の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

Java、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)?それらは同じ長さですが、2番目はそれを書くより自然な方法のようです。
-JohnE

1
@JohnE i++もともとはforループ内にあり、現在の位置にゴルフされたと思います。while小さくはないので、変更する理由はありません。
CJデニス

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

コマンドラインオプションを1としてカウントします。入力はから取得され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))

これにより、f2つの整数を受け取り、入力の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'ます。乗算によってそれぞれaxor合計し、左シフトを有します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));

次に、xリングの不確定要素にGAP変数を割り当てますR。さて、xGAPで言うたびに、システムは、リングの不確定性について話していることを認識しRます。

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

次に、2つの関数があります。これらは相互の逆マップです。これらのマップは両方とも上にありますが、構造を維持するものではないため、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進数を10進数に変換するのと同じ方法で、結果を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;

最後のステップでは、これらの関数を呼び出します。2つの整数入力を取り、それらをリング内の要素に変換し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

ダーツ、34 32バイト

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

単純な再帰的実装。



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 cC ++ではなく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

ただし、コンパイルされた形式でアセンブリ言語を測定すると思います。
ニッサ


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

これは、説明したとおりのアルゴリズムです。thのビットが設定さaれている2^i場所で乗算ib、xorを使用してそれらをすべて加算します。0:64JVMで実行する場合、Ceylonでは整数が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));

ここでは、エイリアスは1バイトだけ安全です。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.