Вам предоставляется машина с двумя 16-битными регистрами, x
и y
. Регистры инициализированы x=1
и y=0
. Единственная операция, которую может выполнить машина, это сложение по модулю 65536. То есть:
x+=y
-x
заменен на(x + y) mod 65536
;y
без измененийy+=x
- аналогично дляy
x+=x
-x
заменен на2x mod 65536
; законно только еслиx
четy+=y
- аналогично дляy
Цель состоит в том, чтобы получить заранее определенное число в одном из регистров (либо, x
либо y
).
Напишите программу или подпрограмму, которая получает число (in stdin
, argv
параметр функции, вершина стека или любое другое обычное место) и выводит программу для получения этого числа. Вывод должен идти stdout
или (если у вашего языка нет stdout
) на любое другое обычное устройство вывода.
Программа вывода может составлять до 100% плюс 2 шага, далеких от оптимальных. То есть, если в самой короткой программе для получения целевого числа есть n
шаги, ваше решение не может быть длиннее, чем 2n+2
. Это ограничение состоит в том, чтобы избегать «слишком простых» решений (например, считая 1, 2, 3, ...), но не требует полной оптимизации; Я ожидаю, что самую короткую программу легче всего найти, но не уверен ...
Например: Вход = 25. Выход:
y+=x
x+=y
x+=y
x+=x
x+=x
x+=x
y+=x
Другой пример: для любого числа Фибоначчи выходные данные имеют этот чередующийся шаблон. Для ввода = 21 выход
y+=x
x+=y
y+=x
x+=y
y+=x
x+=y
y+=x
Самый короткий код (измеряется в байтах) выигрывает.
(эта головоломка была вдохновлена неким кодом для 16-битного процессора, который мне пришлось генерировать недавно)
PS Интересно - для какого числа оптимальная программа самая длинная?
источник
x+=x
законно, только еслиx
это даже? Также для самой короткой программы, я думаю, что-то вроде BFS может работать.x+=x
работает только для четныхx
s, как получается пример для ввода 25 удваивается 3?Ответы:
CJam, 31
Как и ответ @Tobia , мой алгоритм также бесстыдно
украден,вдохновленный ответом @CChak . Но, используя черную магию CJam, мне удалось сделать еще меньшую реализацию алгоритма.Попробуй это здесь.
Golfed:
Ungolfed:
Пожалуйста, исправьте меня, если я ошибаюсь, но я полагал, что операция по модулю 65536, используемая в ответах с похожим алгоритмом, не нужна. Я интерпретировал вопрос так, что мы можем предположить, что входными данными будет действительное 16-разрядное целое число без знака, и любые промежуточные значения или результаты этого алгоритма также будут.
источник
Perl
10797Первый пост, так что здесь идет.
Что соответствует всем критериям добавления в регистр, но я не провел исчерпывающую проверку, чтобы убедиться, что мой ответ всегда был в пределах 2n + 2 от оптимального количества шагов. Впрочем, оно хорошо вписывается в предел для каждого числа Фибоначчи.
Вот более подробная разбивка
Как я уже говорил, это моя первая попытка игры в гольф, поэтому я уверен, что это можно улучшить. Кроме того, я не уверен, должен ли начальный вызов подпрограммы учитываться в рекурсивном вызове или нет, что может привести нас к росту на несколько символов.
Интересно, что мы можем уменьшить код на 11 байтов * и повысить нашу «эффективность» с точки зрения количества операций с регистрами, ослабив требование, что только четные значения могут быть «удвоены». Я включил это для развлечения здесь:
Начать приложение:
Мне очень понравилась эта проблема, и я возился с ней в течение последних нескольких недель. Думаю, я опубликую свои результаты.
Некоторые цифры:
Используя алгоритм BFS, чтобы найти оптимальное решение, в первых 2 ^ 16 числах есть только 18 чисел, которые требуют 23 шага. Это: 58558, 59894, 60110, 61182, 61278, 62295, 62430, 62910, 63422, 63462, 63979, 64230, 64314, 4486, 64510, 64698, 64854, 65295.
Используя рекурсивный алгоритм, описанный выше, «самое трудное» число - 65535 при 45 операциях. (65534 занимает 44, и есть 14 чисел, которые делают 43 шага) 65535 также является наибольшим отклонением от оптимального, 45 против 22. Разница в 23 шага составляет 2n + 1. (Только 2 числа достигают 2n: 65534, 32767, 32751.) За исключением тривиальных (с нулевым шагом) случаев, в определенном диапазоне рекурсивный метод в среднем приблизительно в 1,4 раза превышает оптимальное решение.
Итог: для чисел 1-2 ^ 16 рекурсивный алгоритм никогда не пересекает определенный порог 2n + 2, поэтому ответ верен. Однако я подозреваю, что он станет слишком далеко от оптимального решения для больших регистров / большего количества битов.
Код, который я использовал для создания BFS, был небрежным, занимал много памяти, не комментировался и совершенно сознательно не включался. Так что ... тебе не нужно доверять моим результатам, но я довольно уверен в них.
источник
Python 3, 202 байта
(Спасибо @rationalis за несколько байтов)
Вот очень простое решение. Хотел бы я лучше сыграть в гольф на последней линии, но у меня сейчас нет идей. Позвони с
S(25)
.Программа просто делает простую BFS без кеширования, поэтому работает очень медленно. Вот
S(97)
пример вывода:источник
Dyalog APL, 49 символов / байт *
Алгоритм бесстыдно вдохновлен ответом @CChak .
Пример:
Ungolfed:
* Dyalog APL поддерживает устаревшую кодировку, в которой символы APL отображаются в верхние 128-байтовые значения. Поэтому программу APL, которая использует только символы ASCII и символы APL, можно считать байтами == символов.
источник
Питон, 183
Я не могу гарантировать, что это останется в пределах 2х оптимальной программы для четных чисел, но она эффективна. Для всех допустимых входных данных
0 <= n < 65536
это по сути мгновенно и генерирует программу максимум из 33 инструкций. Для произвольного размера регистраn
(после фиксации этой константы) на это потребуетсяO(n)
не более2n+1
инструкций.Некоторая бинарная логика
Любое нечетное число
n
может быть достигнуто в течение 31 шагов: делатьy+=x
, получатьx,y = 1,1
, а затем продолжает удваиваяx
сx+=x
(для первого удвоения сделатьx+=y
, так какx
нечетно , чтобы начать с).x
таким образом достигнет каждой степени 2 (это просто сдвиг влево), поэтому вы можете установить любой битy
равным 1, добавив соответствующую степень 2. Поскольку мы используем 16-битные регистры и каждый бит, кроме для первого требуется одно удвоение для достижения и одноy+=x
для установки, мы получаем максимум 31 опс.Любое четное число
n
- это просто некоторая степень 2, назовите егоa
, умножьте на нечетное число, назовите егоm
; то естьn = 2^a * m
или эквивалентноn = m << a
. Используйте описанный выше процесс, чтобы получитьm
, затем сбросьтеx
его, сдвинув влево до тех пор, пока не станет 0. Сделайте a,x+=y
чтобы установитьx = m
, а затем продолжайте удваиватьx
, используя в первый разx+=y
и затем используяx+=x
.Что бы там ни было
a
, требуется16-a
сдвиг,x
чтобы получитьy=m
и дополнительныеa
сдвиги, чтобы сброситьx=0
. Другиеa
сдвигиx
произойдут послеx=m
. Таким образом, общее количество16+a
смен используется. Есть до16-a
битов, которые нужно установить, чтобы получитьm
, и каждый из них будет по одномуy+=x
. Наконец, нам нужен дополнительный шаг,x=0
чтобы установить его в mx+=y
. Таким образом, для получения любого четного числа требуется не более 33 шагов.Конечно, вы можете обобщить это на регистр любого размера, и в этом случае он всегда принимает самое большее
2n-1
и2n+1
ops для нечетных и четныхn
битов соответственно.Оптимальность
Этот алгоритм производит программу, близкие к оптимальному (т.е. в пределах ,
2n+2
еслиn
минимальное количество шагов) для нечетных чисел. Для заданного нечетного числаn
, еслиm
th-й бит является первым 1, то любая программа предпринимает как минимумm
шаги, чтобы добраться доx=n
илиy=n
, поскольку операция, которая увеличивает значения регистров быстрее всего,x+=x
илиy+=y
(т.е. удваивается), иm
для получения 1-m
й бит из 1. Поскольку этот алгоритм выполняет не более2m
одного шага (не более двух на удвоение, одно для сдвига и одноy+=x
), любое нечетное число представляется почти оптимальным.Четные числа не так хороши, так как он всегда использует 16 операций для сброса,
x
прежде чем что-либо еще, а 8, например, может быть достигнуто за 5 шагов.Интересно, что приведенный выше алгоритм никогда не использует
y+=y
вообще, такy
как всегда остается нечетным. В этом случае он может найти самую короткую программу для ограниченного набора, состоящего только из 3 операций.тестирование
Я написал простой тест, чтобы проверить, что мое решение действительно дает правильные результаты и никогда не проходит 33 шага для всех допустимых входных данных (
0 <= n < 65536
).Кроме того, я попытался провести эмпирический анализ, чтобы сравнить выходные данные моего решения с оптимальными выходными данными - однако оказывается, что поиск в ширину слишком неэффективен, чтобы получить минимальную длину выходных данных для каждого действительного ввода
n
. Например, использование BFS для поиска выходных данныхn = 65535
не завершается в разумные сроки. Тем не менее я ушелbfs()
и открыт для предложений.Тем не менее, я протестировал свое собственное решение с помощью @ CChak (реализовано здесь на Python как
U
). Я ожидал, что мой будет работать хуже, так как он намного неэффективен для меньших четных чисел, но усреднен по всему диапазону двумя способами, при этом добыча составила в среднем 10,8% до 12,3% короче. Я подумал, что это может быть связано с большей эффективностью моего собственного решения для нечетных чисел, поэтомуV
использует мое для нечетных чисел и @ CChak для четных чисел, ноV
находится между ними (примерно на 10% короче, чем наU
3% длиннееS
).источник
x,y='xy'
это возможно до сих пор. К сожалению, я не могу придумать способc*b+e*2
кратко переписать с%
форматированием.S(2)
очень длинный вывод?S(2)
самый короткий из 19). Я не отслеживаюx
иy
явно, поэтому, несмотря на то, чтоx
достигает второго после второго шага, он продолжает сбрасыватьсяx
на 0. Я чувствую, что должно быть лучшее решение, но пока я не могу думать о один.