ブラインドバイナリ加算器


10

2つのボックスB(x)とがありB(y)、それぞれに不明なビット-0または1が含まれていて、FそれらをX線撮影してB(x^y)xor)の3つ目のボックスを生成できるマシンがあるとします。(およびFも計算できます。-実際には、それらは、機械が実行できる単一の操作のちょうど特別な場合である内積それぞれ、で示さ以下。B(x*y)F()

2つの同じ長さの配列の場合

[B(x[0]), B(x[1]), ..., B(x[n-1])]
[B(y[0]), B(y[1]), ..., B(y[n-1])]

内積は次のように定義されます

B(x[0]*y[0] ^ x[1]*y[1] ^ ... ^ x[n-1]*y[n-1])

それぞれ」は、F()一度に複数ののペアを処理できることを意味します。そして一組からは同じ長さでなければなりません。異なるペアの-sと-sは必ずしも必要ではありません。x[]y[]x[]y[]x[]y[]

ボックスは一意の整数IDで表されます。

JavaScriptでのそれぞれ内積の実装は次のようになります。

var H=[0,1];          // hidden values, indexed by boxId
function B(x) {       // seal x in a new box and return the box id
  return H.push(x)-1;
}
function F(pairs) {   // "inner product each"
  return pairs.map(function (pair) {
    var r = 0, x = pair[0], y = pair[1];
    for (var i = 0; i < x.length; i++) r ^= H[x[i]] * H[y[i]];
    return B(r);
  })
}

(上記を選択した言語に翻訳してください。)

アクセス所与のF()言語のための適切な(これらにアクセスないように実装HまたはB())および2つの整数の16ビットバイナリ表現を構成するボックスIDの二つの配列を所与ab、タスクは、16ビットのバイナリ表現のための生産ボックスIDにありますa+b最小数と(オーバーフローを廃棄)F()コール。

呼び出しF()回数が最も少ないソリューションが優先されます。ネクタイは、呼び出されたx[],y[]ペアの総数を数えることで壊れますF()-少ないほど良いです。それでも同程度の場合は、コードのサイズ(F()およびそのヘルパーの実装を除く)が、従来のコードゴルフの方法で勝者を決定します。回答には、「MyLang、123コール、456ペア、789バイト」のようなタイトルを使用してください。

関数または完全なプログラムを記述します。入力/出力/引数/結果は、適切な形式のint配列です。バイナリ表現はリトルエンディアンまたはビッグエンディアンのいずれかです。いずれかを選択してください。


付録1:チャレンジを少し簡単にするために、ID 0と1のボックスに値0と1が含まれていると想定できます。これにより、たとえば否定(x^1is "not")に役立つ定数が得られます。もちろん、定数の不足を回避する方法はありましたが、残りの課題はとにかく十分に難しいので、この気晴らしを排除しましょう。


付録2:賞金を獲得するには、次のいずれかを行う必要があります。

  • 締め切り前にスコア(コール、ペア、バイト)とコードを投稿する

  • 締め切り前にスコアとコードのsha256ハッシュを投稿してください。締め切り後23時間以内に実際のコードを投稿してください


これを私の選択した言語(Haskell)に翻訳した場合、値の再帰を使用してF1回だけ呼び出すことができます。それは確かに不正行為ですが、それが良い不正行為であるか悪い不正行為であるかはわかりません。
クリスチャンシーバーズ

Haskellではグローバルな状態は歓迎されないことを知っていますが、これを思考実験として考えてみましょう。Fの実装でグローバルカウンターをインクリメントした場合、最後にどれだけ大きくなりますか?-それが私の「コール数」の理解です。
ngn 2017

私はそれを正確に行うことができ、1と表示されますが、コードを使用してJavaScriptに変換することはできませんでした。本質的に私は言ってy=f(x)、それにx依存させましょうy
クリスチャンシーバーズ

私はそれがどのように機能するか理解していないと思います。サンプルコードを見せていただけますか?私のHaskellは貧弱ですが、コードをいじってみれば理解できると思います。
ngn 2017

おそらく、次のタイプを使用してこの問題をモデル化できますか?data Box = B Int deriving (Show); f :: [[[Box]]] -> [Box]実装方法を理解するのにもっと時間が必要ですf(Haskellはここで小文字を強制します)-明日試してみます。
ngn 2017

回答:


6

Python 3、5コール、92ペア、922バイト

Python 3、5コール、134ペア、3120バイト

Python 3、6コール、106ペア、2405バイト

[JavaScript(Node.js)]、9コール、91ペア、1405バイト

JavaScript(Node.js)、16コール、31ペア、378バイト

def add(F,a,b):r=[];p=lambda x:(x,x);q=lambda u,v,t:([u,v]+t[0],[u,v]+t[1]);s=lambda c,k,n:([e[j][n]for j in range(k,-1,-1)]+[f[n]],[c]+f[n-k:n+1]);t=lambda c,k,n:q(a[n],b[n],s(c,k,n-1));z=F([p([a[i],b[i]])for i in range(16)]+[([a[i]],[b[i]])for i in range(16)]);e=[z[0:16]];f=z[16:32];r+=[e[0][0]];c=f[0];z=F([p([a[1],b[1],c]),([e[0][1],f[1]],[c,f[1]])]+[([e[0][i]],[e[0][i-1]])for i in range(3,16)]);r+=[z[0]];c=z[1];e+=[[0]*3+z[2:15]];z=F([p([a[2],b[2],c]),t(c,0,3),s(c,1,3)]+[([e[j][i]],[e[1][i-j-1]])for j in range(2)for i in range(6+j,16)]);r+=z[0:2];c=z[2];e+=u(2,4,z[3:]);z=F([p([a[4],b[4],c])]+[t(c,i,i+5)for i in range(0,3)]+[s(c,3,7)]+[([e[j][i]],[e[3][i-j-1]])for j in range(4)for i in range(12+j,16)]);r+=z[0:4];c=z[4];e+=u(4,8,z[5:]);z=F([p([a[8],b[8],c])]+[t(c,i,i+9) for i in range(0,7)]);return r+z
def u(b,e,z):
	j=0;w=[0]*(e-b)
	for i in range(b,e):w[i-b]=[0]*(i+e)+z[j:j+16-(i+e)];j+=16-(i+e)
	return w

オンラインでお試しください!

第1版 ゴルフではありません。これは、@ ngnのコードを単に改変したものです。

ここでの唯一のアイデアは、オーバーフローを破棄するため、最後のキャリーを計算する必要がないということです。また、の呼び出しはF2つにグループ化されます。多分それらは別の方法でグループ化されるかもしれませんが、基本的な加算アルゴリズムの性質上、ペアの数を大幅に減らすことができるとは思えません。

編集:まだゴルフをしていません。ペアの数は確かに減らすことができ、おそらく呼び出しの数も減らすことができます。sympyの「証明」については、https: //gist.github.com/jferard/864f4be6e4b63979da176bff380e6c62を参照してください

EDIT 2 Pythonに切り替えました。私にとっては読みやすいからです。今、私は一般的な式を持っています、私は5(おそらく4)の呼び出しの制限に達するかもしれないと思います。

編集3 基本的なブリックは次のとおりです。

alpha[i] = a[i] ^ b[i]
beta[i] = a[i] * b[i]
c[0] = beta[0]
r[0] = alpha[0]

一般的な式は次のとおりです。

c[i] = alpha[i]*c[i-1] ^ beta[i]
r[i] = a[i] ^ b[i] ^ c[i-1]

拡張バージョンは次のとおりです。

c[0] = beta[0]
c[1] = alpha[1]*beta[0] ^ beta[1]
c[2] = alpha[2]*alpha[1]*beta[0] ^ alpha[2]*beta[1] ^ beta[2]
c[3] = alpha[3]*alpha[2]*alpha[1]*beta[0] ^ alpha[3]*alpha[2]*beta[1] ^ alpha[3]*beta[2] ^ beta[3]
...
c[i] = alpha[i]*...*alpha[1]*beta[0] ^ alpha[i]*...*alpha[2]*beta[1] ^ .... ^ alpha[i]*beta[i-1] ^ beta[i]

5回の呼び出しが私には限界のようです。今、私はペアを外してゴルフをする少しの仕事があります!

編集4私はこれをゴルフした。

ゴルフされていないバージョン:

def add(F, a, b):
    r=[]
    # p is a convenient way to express x1^x2^...x^n
    p = lambda x:(x,x)
    # q is a convenient way to express a[i]^b[i]^carry[i-1]
    q = lambda u,v,t:([u,v]+t[0],[u,v]+t[1])

    # step1: the basic bricks
    z=F([p([a[i],b[i]]) for i in range(16)]+[([a[i]],[b[i]]) for i in range(16)])
    alpha=z[0:16];beta=z[16:32]
    r.append(alpha[0])
    c = beta[0]

    # step 2
    z=F([
        p([a[1],b[1],c]),
        ([alpha[1],beta[1]],[c,beta[1]])
        ]+[([alpha[i]],[alpha[i-1]]) for i in range(3,16)])
    r.append(z[0])
    c = z[1] # c[1]
    alpha2=[0]*3+z[2:15]
    assert len(z)==15, len(z)

    # step 3
    t0=([alpha[2],beta[2]],[c,beta[2]])
    t1=([alpha2[3],alpha[3],beta[3]],[c,beta[2],beta[3]])
    z=F([
        p([a[2],b[2],c]),
        q(a[3],b[3],t0),
        t1]+
        [([alpha[i]],[alpha2[i-1]]) for i in range(6,16)]+
        [([alpha2[i]],[alpha2[i-2]]) for i in range(7,16)])
    r.extend(z[0:2])
    c = z[2] # c[3]
    alpha3=[0]*6+z[3:13]
    alpha4=[0]*7+z[13:22]
    assert len(z)==22, len(z)

    # step 4
    t0=([alpha[4],beta[4]],[c,beta[4]])
    t1=([alpha2[5],alpha[5],beta[5]],[c,beta[4],beta[5]])
    t2=([alpha3[6],alpha2[6],alpha[6],beta[6]],[c,beta[4],beta[5],beta[6]])
    t3=([alpha4[7],alpha3[7],alpha2[7],alpha[7],beta[7]],[c,beta[4],beta[5],beta[6],beta[7]])
    z=F([
        p([a[4],b[4],c]),
        q(a[5],b[5],t0),
        q(a[6],b[6],t1),
        q(a[7],b[7],t2),
        t3]+
        [([alpha[i]],[alpha4[i-1]]) for i in range(12,16)]+
        [([alpha2[i]],[alpha4[i-2]]) for i in range(13,16)]+
        [([alpha3[i]],[alpha4[i-3]]) for i in range(14,16)]+
        [([alpha4[i]],[alpha4[i-4]]) for i in range(15,16)])
    r.extend(z[0:4])
    c = z[4] # c[7]
    alpha5 = [0]*12+z[5:9]
    alpha6 = [0]*13+z[9:12]
    alpha7 = [0]*14+z[12:14]
    alpha8 = [0]*15+z[14:15]
    assert len(z) == 15, len(z)

    # step 5
    t0=([alpha[8],beta[8]],[c,beta[8]])
    t1=([alpha2[9],alpha[9],beta[9]],[c,beta[8],beta[9]])
    t2=([alpha3[10],alpha2[10],alpha[10],beta[10]],[c,beta[8],beta[9],beta[10]])
    t3=([alpha4[11],alpha3[11],alpha2[11],alpha[11],beta[11]],[c,beta[8],beta[9],beta[10],beta[11]])
    t4=([alpha5[12],alpha4[12],alpha3[12],alpha2[12],alpha[12],beta[12]],[c,beta[8],beta[9],beta[10],beta[11],beta[12]])
    t5=([alpha6[13],alpha5[13],alpha4[13],alpha3[13],alpha2[13],alpha[13],beta[13]],[c,beta[8],beta[9],beta[10],beta[11],beta[12],beta[13]])
    t6=([alpha7[14],alpha6[14],alpha5[14],alpha4[14],alpha3[14],alpha2[14],alpha[14],beta[14]],[c,beta[8],beta[9],beta[10],beta[11],beta[12],beta[13],beta[14]])
    t7=([alpha8[15],alpha7[15],alpha6[15],alpha5[15],alpha4[15],alpha3[15],alpha2[15],alpha[15],beta[15]],[c,beta[8],beta[9],beta[10],beta[11],beta[12],beta[13],beta[14],beta[15]])

    z=F([
        p([a[8],b[8],c]),
        q(a[9],b[9],t0),
        q(a[10],b[10],t1),
        q(a[11],b[11],t2),
        q(a[12],b[12],t3),
        q(a[13],b[13],t4),
        q(a[14],b[14],t5),
        q(a[15],b[15],t6)
    ])
    r.extend(z)
    return r

オンラインでお試しください!


非常に良い:)私が意図的に省略した2つの簡単な最適化を見つけました。「ペアの数を大幅に減らすことができるとは思えません」-勝つための最初の基準は、へのコール数ですF()。それらを大幅に減らす方法があることを保証します(これがこの課題の最も難しい部分です)。次に、ペアの数を最適化する余地があり、最後にコードをゴルフすることになります(ただし、これは最も重要でない基準です)。
ngn 2017

はい、分かりました!遅かれ早かれ、あなたはそのようなものを得ました:... + x * y * z + ...。これを使用Fして評価することはできませんx * yが、前のF呼び出しで計算した場合は、次のことを行うだけです... + (x * y) * z + ...(これはの形式に一致しますF)。sympyで遊んで、なんとかして電話をかけずに(step1:計算r0、c0、r1; step2:計算c1といくつかのaux値; step3:計算r2、c2、r3、c3)、今私は一般を探しています解決。
jferard 2017

はい、言い換えれば、出力ビットは入力ビットの2より大きい次数の多項式です。内積は、最大で、m次多項式とn次多項式を組み合わせて(m + n)次多項式にすることができます。急がないでください-数時間で賞金を設定できるようになります:)
ngn

上記の付録2を利用することを検討してください。または、誰かがあなたのコードをコピーしてスペースを削除し、それを再投稿した場合、技術的にはボーナスを彼らに与える必要があります。
ngn '30

2
記録では、解が32次多項式を必要とするため、5つ未満の呼び出しを使用することは不可能です。(入力ビットの任意の関数に対応する多項式は一意です。)
ニトロドン2017

2

Haskell、1コール(不正行為???)、32ペア(改善される可能性があります)、283バイト(同じ)

私に腹を立てないでください、私はこれで勝ちたくありませんが、私が話していることを説明する挑戦への発言で励まされました。

ボックスの追加、呼び出しとペアのカウントを処理するために状態モナドを使用しようとしましたが、うまくいきましたが、その設定でソリューションを機能させることができませんでした。したがって、コメントでも提案されていることを行いました。データをデータコンストラクタの背後に隠して、覗き見しないでください。(クリーンな方法は、別個のモジュールを使用し、コンストラクターをエクスポートしないことです。)このバージョンには、はるかに単純であるという利点があります。

私たちはビットの箱について話しているので、私はBoolそれらに値を入れました。私はゼロビットzeroの与えられたボックスとして定義します-a oneは必要ありません。

import Debug.Trace

data B = B { unB :: Bool }

zero :: B
zero = B False

f :: [([B],[B])] -> [B]
f pairs =  trace ("f was called with " ++ show (length pairs) ++ " pairs") $
           let (B i) &&& (B j) = i && j
           in map (\(x,y) ->  B ( foldl1 (/=) (zipWith (&&&) x y))) pairs

デバッグ関数traceを使用して、f呼び出された頻度とペアの数を確認しています。&&&パターンマッチングによってボックスを調べます。値に/= 使用される不等式Boolxorです。

bits :: Int -> [Bool]
bits n = bitsh n 16
  where bitsh _ 0 = []
        bitsh n k = odd n : bitsh (n `div` 2) (k-1)

test :: ( [B] -> [B] -> [B] ) -> Int -> Int -> Bool
test bba n m = let x = map B (bits n)
                   y = map B (bits m)
                   r = bba x y
                   res = map unB r
               in res==bits(n+m)

このtest関数は、最初の引数としてブラインドバイナリ加算器を取り、次に加算がテストされる2つの数値を受け取ります。Boolテストが成功したかどうかを示すを返します。最初に入力ボックスが作成され、次に加算器が呼び出され、結果が(でunB)ボックス化解除され、期待される結果と比較されます。

2つの加算器、サンプルソリューションを実装したsimpleので、デバッグ出力が正しく機能していることがわかりますvalrec。また、私のソリューションは値再帰を使用しています。

simple a b = let [r0] = f [([a!!0,b!!0],[a!!0,b!!0])]
                 [c]  = f [([a!!0],[b!!0])]
             in loop 1 [r0] c
             where loop 16 rs _ = rs
                   loop i  rs c = let [ri] = f [([a!!i,b!!i,c],[a!!i,b!!i,c])]
                                      [c'] = f [([a!!i,b!!i,c],[b!!i,c,a!!i])]
                                  in loop (i+1) (rs++[ri]) c'

valrec a b =
    let res = f (pairs res a b)
    in [ res!!i | i<-[0,2..30] ]
  where pairs res a b =
           let ts = zipWith3 (\x y z -> [x,y,z])
                             a b (zero : [ res!!i | i<-[1,3..29] ]) in
           [ p | t@(h:r) <- ts, p <- [ (t,t), (t,r++[h]) ] ]

resそれ自体の観点から私がどのように定義しているかを参照してください。それは結び目結ぶこととしても知られています

これで、がf1回だけ呼び出される方法を確認できます。

*Main> test valrec 123 456
f was called with 32 pairs
True

または、32回呼び出されるvalrecことsimpleを確認するために置き換えfます。

オンラインでお試しください!(トレース出力は「デバッグ」の下に表示されます)


ここに怒りはありません:)では、私が正しく理解していれば、への引数fは、それを反復するときに具体化する遅延の可能性のある無限のリストですか?チャレンジの精神に反していると思います。- i+1thに対応する結果を取得した、-st引数として何を渡すかについての決定を延期することができますif完全に
具現化された

同意する。@jferardは、そのようなトリックによって無効にされるべきではない素晴らしい仕事をしました。しばらくはfポイントではない、無限の入力を(イェーイ、無限のビットストリームを追加!)、取ることができます。ああ、そして実際にtraceメッセージは、長さが有限であり、最初に知られていることを保証します。また、私は延期された決定があるとは言いません:要求されたように私はただ盲目的にボックスをシャッフルしているだけで、すべてが事前に計画されました。そして、それは引数の順序に関するものではないことに注意してくださいres。最初に結果が含まれ、次にキャリービットが含まれるように変更できます。
クリスチャンシーバーズ2017

「私はただ盲目的に箱をシャッフルしている」-あなたが電話から箱を入手したとしましょうf。への同じ呼び出しで別の引数としてそのボックスをフィードバックしますfか?
ngn '30

はい、そうです。それが再帰の価値です。あなたはその権利を持っていました。それは怠惰を使用しており、完全に具体化されていない引数を使用できるという事実です(その説明が好きです)。挑戦の明白な精神を考えると、それは-発表されたように-明らかに不正行為です。それが独創的または注目に値すると思う場合、それは不正行為が良いと主張するかもしれません。
クリスチャンシーバーズ

それは確かに良い種類のものです-明らかにあなたはここで騙すつもりはありません 関数型プログラミングにおける怠惰は美しい概念であり、その有効な用途があります。数年前にいくつかのHaskellを学習しようとしたとき、フィボナッチ数列の「結び目」を表す1ライナーに非常に感銘を受けたことを覚えています。
ngn 2017

0

JavaScript、32コール、32ペア、388バイト

Dyalog APL、32コール、32ペア、270バイト

これは、テンプレートとして使用できる単純なサンプルソリューションです。

バイトカウントには、「BEGIN / END SOLUTION」で囲まれたセクションのみを含める必要があることに注意してください。

説明:

私はリトルエンディアンのビット順序を選択しました(x[0]最下位ビットです)。

単一ビット加算mod 2はF([[[x,y],[x,y]]])(つまり:x*x ^ y*y-乗算mod 2はべき等)として実現でき、バイナリ乗算はとして実現できることに注意してくださいF([[[x],[y]]])

ビットを最下位から最上位までトラバースし、各ステップで結果ビットとキャリーを計算します。

#!/usr/bin/env node
'use strict'
let H=[0,1]
,B=x=>H.push(x)-1
,nCalls=0
,nPairs=0
,F=pairs=>{
  nCalls++;nPairs+=pairs.length
  return pairs.map(([x,y])=>{let s=0;for(let i=0;i<x.length;i++)s^=H[x[i]]*H[y[i]];return B(s)})
}

// -----BEGIN SOLUTION-----
var f=(a,b)=>{
  var r=[], c // r:result bits (as box ids), c:carry (as a box id)
  r[0]=F([[[a[0],b[0]],[a[0],b[0]]]])          // r0 = a0 ^ b0
  c=F([[[a[0]],[b[0]]]])                       // c = a0*b0
  for(var i=1;i<16;i++){
    r.push(F([[[a[i],b[i],c],[a[i],b[i],c]]])) // ri = ai ^ bi ^ c
    c=F([[[a[i],b[i],c],[b[i],c,a[i]]]])       // c = ai*bi ^ bi*c ^ c*ai
  }
  return r
}
// -----END SOLUTION-----

// tests
let bits=x=>{let r=[];for(let i=0;i<16;i++){r.push(x&1);x>>=1}return r}
,test=(a,b)=>{
  console.info(bits(a))
  console.info(bits(b))
  nCalls=nPairs=0
  let r=f(bits(a).map(B),bits(b).map(B))
  console.info(r.map(x=>H[x]))
  console.info('calls:'+nCalls+',pairs:'+nPairs)
  console.assert(bits(a+b).every((x,i)=>x===H[r[i]]))
}

test(12345,6789)
test(12,3)
test(35342,36789)

Dyalog APLでも同じ(ただし、ランダムなボックスIDを使用):

⎕io←0⋄K←(V←⍳2),2+?⍨1e6⋄B←{(V,←⍵)⊢K[≢V]}⋄S←0⋄F←{S+←1,≢⍵⋄B¨2|+/×/V[K⍳↑⍉∘↑¨⍵]}
⍝ -----BEGIN SOLUTION-----
f←{
  r←F,⊂2⍴⊂⊃¨⍺⍵        ⍝ r0 = a0 ^ b0
  c←⊃F,⊂,¨⊃¨⍺⍵        ⍝ c = a0*b0
  r,⊃{
    ri←⊃F,⊂2⍴⊂⍺⍵c     ⍝ ri = ai ^ bi ^ c
    c⊢←⊃F,⊂(⍺⍵c)(⍵c⍺) ⍝ c = ai*bi ^ bi*c ^ c*ai
    ri
  }¨/1↓¨⍺⍵
}
⍝ -----END SOLUTION-----
bits←{⌽(16⍴2)⊤⍵}
test←{S⊢←0⋄r←⊃f/B¨¨bits¨⍺⍵
      ⎕←(↑bits¨⍺⍵)⍪V[K⍳r]⋄⎕←'calls:' 'pairs:',¨S
      (bits⍺+⍵)≢V[K⍳r]:⎕←'wrong!'}
test/¨(12345 6789)(12 3)(35342 36789)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.