Представьте , у вас есть две коробки 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: Чтобы получить награду, вы должны выполнить одно из следующих действий:
опубликовать ваш счет (звонки, пары, байты) и ваш код до истечения срока
опубликуйте свой счет и хэш кода sha256 до истечения срока; затем опубликовать действительный код в течение 23 часов после крайнего срока
F
только один раз. Это наверняка было бы обманом, но я не уверен, будет ли это хорошим обманом или плохим обманом.y=f(x)
и пустьx
зависит отy
.data Box = B Int deriving (Show); f :: [[[Box]]] -> [Box]
Мне нужно больше времени, чтобы выяснить, как реализоватьf
(Haskell здесь вводит нижний регистр) - завтра попробую.Ответы:
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 байтовПопробуйте онлайн!
ПЕРВАЯ ВЕРСИЯ Хорошо, это не игра в гольф. Это просто адаптация кода @ ngn.
Единственная идея в том, что вам не нужно вычислять последний перенос, так как вы отбрасываете переполнение. Также звонки
F
сгруппированы по двум. Может быть, они могут быть сгруппированы по-другому, но я сомневаюсь, что вы можете значительно сократить количество пар из-за природы основного алгоритма сложения.РЕДАКТИРОВАТЬ : Все еще не в гольф. Количество пар, конечно, можно уменьшить, и, возможно, количество звонков тоже. См. Https://gist.github.com/jferard/864f4be6e4b63979da176bff380e6c62 для «доказательства» с sympy.
РЕДАКТИРОВАТЬ 2 Переключился на Python, потому что он более читабелен для меня. Теперь у меня есть общая формула, я думаю, что могу достичь предела 5 (может быть, 4) вызовов.
РЕДАКТИРОВАТЬ 3 Вот основные кирпичи:
Общая формула:
Расширенная версия:
5 звонков кажутся мне пределом. Теперь у меня есть небольшая работа по удалению пар и игре в гольф!
РЕДАКТИРОВАТЬ 4 Я играл в гольф это.
Безголовая версия:
Попробуйте онлайн!
источник
F()
. Я гарантирую, что есть способ значительно сократить их (это самая сложная часть этой задачи), и тогда будет место для оптимизации количества пар и, наконец, конечно, игра в гольф (но это наименее важный критерий).... + x * y * z + ...
. Мы не можем использовать егоF
для оценки, но если мы вычислилиx * y
с помощью предыдущегоF
вызова, нам просто нужно сделать:... + (x * y) * z + ...
(соответствует форматуF
). Играя с sympy, мне удалось сэкономить вызов (step1: вычислить r0, c0, r1; step2: вычислить c1 и некоторые вспомогательные значения; step3: вычислить r2, c2, r3, c3), и теперь я разыскиваю общий решение.Haskell, 1 вызов (измена ???), 32 пары (можно улучшить), 283 байта (то же самое)
Пожалуйста, не сердитесь на меня, я не хочу победить с этим, но я был воодушевлен в замечаниях к задаче объяснить, о чем я говорил.
Я пытался использовать монаду состояний для обработки полей добавления и подсчета вызовов и пар, и это сработало, но мне не удалось заставить мое решение работать в таких условиях. Поэтому я сделал то, что было предложено в комментариях: просто спрятать данные за конструктором данных и не заглядывать. (Чистым способом было бы использовать отдельный модуль, а не экспортировать конструктор.) Преимущество этой версии в том, что она намного проще.
Поскольку мы говорим о коробках с битами, я вкладываю
Bool
в них значения. Я определяюzero
как данное поле с нулевым битом - aone
не требуется.Мы используем функцию отладки,
trace
чтобы увидеть, как частоf
вызывался и сколько пар.&&&
смотрю в коробки по сопоставлению с образцом, неравенство/=
используется наBool
значенияхxor
.test
Функция принимает слепой двоичный сумматор в качестве первого аргумента, а затем два числа , для которых испытывается дополнение. ВозвращаетBool
указание, был ли тест успешным. Сначала создаются поля ввода, затем вызывается сумматор, результат распаковывается (сunB
) и сравнивается с ожидаемым результатом.Я реализовал два сумматора, пример решения
simple
, чтобы мы могли видеть, что выходные данные отладки работают правильно, и мое решение использует рекурсию значенийvalrec
.Видишь, как я себя определяю
res
? Это также известно как завязывание узла .Теперь мы можем видеть, как
f
вызывается только один раз:Или замените
valrec
,simple
чтобы увидетьf
вызов 32 раза.Попробуйте онлайн! (вывод трассировки отображается в разделе «Отладка»)
источник
f
является ленивый, потенциально бесконечный список, который материализуется, когда вы перебираете его? Я боюсь , что это противоречит духу вызова - он позволяет отложить решение о том, что необходимо передать в качествеi+1
-го аргумента сезама после вы получили результат , соответствующийi
-й. Гораздо интереснее узнать, сколько звонковf
вам понадобится с полностью материализованными неизменными аргументами :)f
может принимать бесконечный ввод (добавить бесконечные потоки бит, да!), Это не главное. Да, и на самом делеtrace
сообщение гарантирует, что длина конечна и известна в начале. Также я бы не сказал, что есть отложенное решение: все было спланировано заранее, так как требовал, чтобы я просто слепо тасовал коробки. И обратите внимание, это не о порядке аргументов: я мог бы изменить его так, чтобы онres
содержал сначала результат, а затем биты переноса.f
; Вы возвращаете эту коробку как еще один аргумент в том же вызовеf
?JavaScript, 32 вызова, 32 пары, 388 байтов
Dyalog APL, 32 вызова, 32 пары, 270 байтов
Это простое примерное решение, которое может служить шаблоном.
Обратите внимание, что в число байтов должен входить только раздел, заключенный в «BEGIN / END SOLUTION».
Объяснение:
Я выбрал порядок
x[0]
младших битов (младший бит).Заметьте, что однобитное сложение мод 2 может быть реализовано как
F([[[x,y],[x,y]]])
(то есть:x*x ^ y*y
- умножение мод 2 идемпотентно) и двоичное умножение какF([[[x],[y]]])
.Мы перебираем биты от наименее значимого к наиболее значимому и на каждом этапе вычисляем бит результата и перенос.
То же самое в Dyalog APL (но с использованием рандомизированных идентификаторов ящиков):
источник