Сліпа двійкова суматор


10

Уявіть, що у вас є дві скриньки, B(x)і B(y)кожна з яких містить невідомий біт - 0 або 1, і машина, Fяка може рентгенограмувати їх і виробляти третю коробку для B(x^y)( xor ). Fможе також обчислити B(x*y)( і ). Насправді це лише окремі випадки однієї операції, яку може виконати машина - внутрішній виріб кожен , позначений F()нижче.

Для двох масивів однакової довжини

[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()може обробляти кілька пар x[], y[]на одному диханні. x[]І y[]від однієї пари повинні бути однакової довжини; x[]-s і y[]-s з різних пар не обов'язково.

Поле представлено унікальними цілими ідентифікаторами.

Реалізація внутрішнього продукту кожного з 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()) та надано два масиви ідентифікаторів вікна, що складаються з 16-бітових бінарних представлень двох цілих чисел, aі bваше завдання - створити ідентифікатори поля для 16-бітного бінарного представлення з a+b(відмова від переповнення) з мінімальною кількістю F()дзвінків.

Виграє рішення, яке викликає F()найменший раз. Зв'язки будуть розбиті підрахунком загальної кількості x[],y[]пар, з якими F()називались - менше краще. Якщо все-таки зв'язано, розмір вашого коду (за винятком реалізації F()та його помічників) визначає переможця традиційним кодом для гольфу. Будь-ласка, використовуйте назву на зразок "MyLang, 123 дзвінки, 456 пар, 789 байт" для своєї відповіді.

Напишіть функцію або повну програму. Вхід / вихід / аргументи / результат - це масиви int у будь-якому розумному форматі. Бінарне представлення може бути мало- або великим-ендіанським - виберіть його.


Додаток 1: Щоб зробити виклик трохи простішим, ви можете припустити, що поля з ідентифікаторами 0 і 1 містять значення 0 і 1. Це дає вам константи, корисні, наприклад, для заперечення ( x^1це "не"). Звичайно, існували шляхи навколо нестачі констант, але решта викликів все одно досить важка, тому давайте усунемо це відволікання.


Додаток 2: Щоб виграти виграш, ви повинні виконати одну з наступних дій:

  • опублікуйте свій рахунок (дзвінки, пари, байти) та свій код до встановленого терміну

  • опублікуйте свій рахунок та ша256 хеш вашого коду до встановленого терміну; потім опублікуйте фактичний код протягом 23 годин після встановленого терміну


Якби я переклав це моєю мовою на вибір (Haskell), я міг би використовувати рекурсію значення та дзвонити Fлише один раз. Це, безумовно, було б обманом, але я не впевнений, чи це було б добре читання чи погана обманка.
Крістіан Сіверс

Я знаю, що глобальна держава не вітається в Хаскеллі, але дозвольте мені поставити це як мислительний експеримент: якби я посилив глобальний лічильник у впровадженні F, на скільки він би виріс у кінці? - це моє розуміння "кількості дзвінків".
ngn

Я міг би зробити це саме так, і він би сказав 1. Але це не вдалося перевести назад у JavaScript за допомогою вашого коду. По суті, я б сказав, y=f(x)і нехай xзалежать від цього y.
Крістіан Сіверс

Боюсь, я не розумію, як це буде працювати. Чи можете ви показати, зразок коду? Мій Haskell поганий, але я впевнений, що можу це зрозуміти, чи можу я грати з кодом.
ngn

Можливо, ми можемо використовувати наступні типи для моделювання цієї проблеми? data Box = B Int deriving (Show); f :: [[[Box]]] -> [Box]Мені знадобиться більше часу, щоб розібратися, як реалізувати f(Haskell силі малі тут) - я спробую завтра.
ngn

Відповіді:


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

Спробуйте в Інтернеті!

ПЕРША ВЕРСІЯ Гаразд, це не гольф. Це лише адаптація коду @ ngn.

Єдина ідея тут полягає в тому, що вам не потрібно обчислювати останнє перенесення, оскільки ви відкидаєте переповнення. Також дзвінки групи Fзгруповані по два. Можливо, вони можуть бути згруповані іншим способом, але я сумніваюся, що ви можете значно зменшити кількість пар, в силу характеру алгоритму базового додавання.

EDIT : Все ще не гольф. Кількість пар, безумовно, може бути зменшена, і, ймовірно, і кількість дзвінків. Дивіться https://gist.github.com/jferard/864f4be6e4b63979da176bff380e6c62 для "доказування" із співчуттям.

EDIT 2 Переключився на Python, тому що він для мене більш читабельний. Тепер я отримав загальну формулу, я думаю, я можу досягти межі 5 (можливо, 4) дзвінків.

EDIT 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 дзвінків здається для мене лімітом. Зараз у мене є невелика робота по вилученню пар і гольфу!

EDIT 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

Спробуйте в Інтернеті!


Дуже добре :) Ви знайшли дві прості оптимізації, які я спеціально залишив. "Я сумніваюся, що ви можете значно зменшити кількість пар" - зауважте, що першим критерієм виграшу є кількість дзвінків до F(). Я гарантую, що є спосіб їх значно зменшити (це найважча частина цього завдання), і тоді буде місце для оптимізації кількості пар, і нарешті, звичайно, гольфу код (але це найменш важливий критерій).
ngn

Добре. Я зрозумів! Рано чи пізно, ви отримали що - щось на зразок цього: ... + 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

Так, іншими словами: вихідні біти є поліномами ступеня, що перевищує 2 у вхідних бітах. Внутрішній продукт може поєднувати поліном m-ступеня та n-ступеня в поліном (m + n) -градуси, максимум. Не поспішайте - через кілька годин я зможу влаштувати
щедроту

Ви можете розглянути можливість використання додатку 2 вище. Або ще: якщо хтось копіює ваш код, видаляє пробіл і повторно розміщує його, технічно мені доведеться присвоїти їм бонус.
ngn

2
Для запису неможливо використовувати менше п’яти викликів, оскільки для вирішення потрібен поліном 32 ступеня. (Поліном, що відповідає будь-якій функції вхідних бітів, унікальний.)
Nitrodon

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дзвонили та скільки пар. &&&заглядає в поля за відповідності шаблону, нерівність, яка /= використовується для Boolзначень, є xor.

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Функція приймає сліпий двійковий суматор в якості першого аргументу, а потім два числа , для яких випробовується доповнення. Він повертає Boolвказівку, чи був тест успішним. Спочатку створюються поля введення, потім викликається суматор, результат нескладається (з unB) і порівнюється з очікуваним результатом.

Я реалізував два суматори, зразок рішення 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з точки зору себе? Це також відомо як зав'язування вузла .

Тепер ми можемо побачити, як fназивається лише один раз:

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

Або замініть valrecна, simpleщоб побачити, fяк викликали 32 рази.

Спробуйте в Інтернеті! (вихідний трасування відображається в розділі "Налагодження")


Ніякого гніву тут :) Отже, якщо я правильно зрозумів, аргумент f- це ледачий, потенційно нескінченний список, який матеріалізується, коли ви повторюєте його? Я боюся, що це суперечить духу виклику - це дозволяє відкласти рішення про те, що потрібно подати як i+1-йший аргумент до того, як ви отримаєте результат, відповідний i-th. Набагато цікавіше дізнатися, скільки дзвінків fвам знадобиться з повністю матеріалізованими, незмінними аргументами :)
ngn

Я згоден. @jferard зробив дивовижну роботу, яку не слід визнавати такою хитрістю недійсною. Хоча fможна взяти нескінченний вклад (додайте нескінченні потоки бітів, так!), Це не в цьому справа. О, і насправді traceповідомлення гарантує, що довжина є кінцевою та відомою на початку. Крім того, я б не сказав, що є відкладене рішення: все було заплановано достроково, як того вимагали, я просто сліпо перетасовую коробки. І зауважте, мова не йде про порядок аргументів: я міг би змінити його таким чином, щоб він resмістив спочатку результат, а потім біт перенесення.
Крістіан Сіверс

"Я просто сліпо перетасовую коробки" - Припустимо, ви отримали коробку від дзвінка f; ви повертаєте це поле як інший аргумент у тому самому дзвінку f?
ngn

Так. Саме це і є рекурсія значення. Ви мали таке право: це використання лінивості та того, що я можу використовувати аргументи, які не повністю матеріалізовані (мені подобається цей опис). З огляду на очевидний дух виклику, це - як було оголошено - явно обман. Якщо хтось думає, що це винахідливе або примітне, можна стверджувати, що це добре обман.
Крістіан Сіверс

Це, безумовно, хорошого виду - очевидно, ви тут не маєте наміру обманювати. Лінь у функціональному програмуванні - прекрасна концепція, і вона має свої чинності. Коли я кілька років тому намагався навчитись Haskell, я пам'ятаю, що був дуже вражений однолінійкою, яка "зав'язує вузол" для чисел Фібоначчі.
ngn

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- модуль множення 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 (але з використанням рандомізованих ідентифікаторів поля):

⎕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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.