объяснение
Befunge - это двумерная программа, использующая стеки .
Это означает, что для выполнения 5 + 6 вы пишете 56+
, что означает:
56+
5 push 5 into stack
6 push 6 into stack
+ pop the first two items in the stack and add them up, and push the result into stack
(to those of you who do not know stacks, "push" just means add and "pop" just means take off)
Тем не менее, мы не можем вставить номер 56
прямо в стек.
Чтобы сделать это, мы должны написать 78*
вместо этого, который умножает 7
и 8
и помещает продукт в стек.
Детали
Для каждого числа от 1
доn
найдите строку, состоящую только из этих символов: 0123456789+-*/:
(я бы не использовал %
по модулю.)
Цель состоит в том, чтобы найти короткую строку, которая может представлять число, используя формат, описанный выше.
Например, если вход 123
, то выход будет 67*9:*+
. Результат должен оцениваться слева направо.
Если имеется более одного приемлемого выхода (например, 99*67*+
, также приемлемо), любой может быть напечатан (без бонуса за печать всех из них).
Дальнейшее объяснение
Если вы все еще не понимаете, как 67*9:*+
оценивать 123
, вот подробное объяснение.
stack |operation|explanation
67*9:*+
[6] 6 push 6 to stack
[6,7] 7 push 7 to stack
[42] * pop two from stack and multiply, then put result to stack
[42,9] 9 push 9 to stack
[42,9,9] : duplicate the top of stack
[42,81] * pop two from stack and multiply, then put result to stack
[123] + pop two from stack and add, then put result to stack
TL; DR
Программа должна найти самый короткий строку, которая может представлять ввод (число), используя формат, указанный выше.
SCORING
- Мы уже сделали это в кратчайшем количестве кода . На этот раз размер не имеет значения.
- На выбранном вами языке должен быть бесплатный компилятор / интерпретатор для моей операционной системы (Windows 7 Enterprise).
- Бонус, если вы включите ссылку на компилятор / интерпретатор (я слишком ленив).
- Если возможно, пожалуйста, включите таймер для моего удобства. Выход от таймера действителен.
- Счет будет самым большим
n
за 1 минуту. - Это означает, что программа должна распечатать требуемое представление из
1
далее. - Нет жесткого кодирования, кроме
0
как9
.
(больше) СПЕЦИФИКАЦИЯ
- Программа недействительна, если она выводит строку длиннее, чем необходимо для любого числа.
1/0=ERROR
5/2=2
,(-5)/2=-2
,(-5)/(-2)=2
,5/(-2)=-2
Disambiguation
-
Это second-top minus top
означает , что 92-
возвращается7
.
Аналогично, /
это second-top divide top
означает, что 92/
возвращает4
.
Пример программы
Lua
Использует поиск в глубину.
local function div(a,b)
if b == 0 then
return "error"
end
local result = a/b
if result > 0 then
return math.floor(result)
else
return math.ceil(result)
end
end
local function eval(expr)
local stack = {}
for i=1,#expr do
local c = expr:sub(i,i)
if c:match('[0-9]') then
table.insert(stack, tonumber(c))
elseif c == ':' then
local a = table.remove(stack)
if a then
table.insert(stack,a)
table.insert(stack,a)
else
return -1
end
else
local a = table.remove(stack)
local b = table.remove(stack)
if a and b then
if c == '+' then
table.insert(stack, a+b)
elseif c == '-' then
table.insert(stack, b-a)
elseif c == '*' then
table.insert(stack, a*b)
elseif c == '/' then
local test = div(b,a)
if test == "error" then
return -1
else
table.insert(stack, test)
end
end
else
return -1
end
end
end
return table.remove(stack) or -1
end
local samples, temp = {""}, {}
while true do
temp = {}
for i=1,#samples do
local s = samples[i]
if eval(s) ~= -1 or s == "" then for n in ("9876543210+-*/:"):gmatch(".") do
table.insert(temp, s..n)
end end
end
for i=1,#temp do
local test = eval(temp[i])
if input == test then
print(temp[i])
return
end
end
samples = temp
end
источник
56
непосредственно в стек, как мы можем вставить78
в стек?56
пятьдесят шесть непосредственно в стек, но мы можем поместить7
семь и8
восемь отдельно в стек.56
Befunge, вы нажимаете цифры , и в итоге вы получаете стек[5, 6]
. Чтобы получить число 56, вы должны нажать7
, затем8
на стек, а затем умножить их, чтобы получить число 56 в стеке.:
все усложняется, поэтому я бы порекомендовал предоставить хороший список тестовых примеров, например86387
Ответы:
C ++, взрывая всю память на компьютере рядом с вами
Генерирует самую короткую строку, где вычисление нигде не вызывает переполнения 32-разрядного целого числа со знаком (поэтому все промежуточные результаты находятся в диапазоне
[-2147483648, 2147483647]
В моей системе это генерирует решение для всех чисел до
483432
30 секунд включительно при использовании памяти 1.8G. Еще большее число быстро взорвет использование памяти. Самое большое число, которое я могу обработать в моей системе, это5113906
. Расчет занимает почти 9 минут и 24ГБ. Когда это заканчивается, у него есть решение для398499338
значений, около 9% всех 32-битных целых (положительных и отрицательных)Требуется компилятор C ++ 11. На Linux скомпилируйте с:
Добавьте
-DINT64
в качестве опции использование 64-разрядного целочисленного диапазона вместо 32-разрядного для промежуточных результатов (это будет использовать примерно на 50% больше времени и памяти). Это требует встроенного 128-битного типа. Возможно, вам придется изменить тип GCC__int128
. Нет результата по крайней мере в диапазоне[1..483432]
изменении , позволяя получить более крупные промежуточные результаты.добавлять
-DOVERFLOW
в качестве опции, чтобы не использовать больший целочисленный тип для проверки на переполнение. Это дает эффект переполнения и переноса значений.Если в вашей системе есть tcmalloc ( https://github.com/gperftools/gperftools ), вы можете связать ее с программой, которая, как правило, немного быстрее и использует немного меньше памяти. В некоторых системах UNIX вы можете использовать предварительную загрузку, например
Основное использование: сгенерируйте и напечатайте все числа до цели:
Параметры:
-a
Также распечатайте все числа, которые были сгенерированы при разработке цели-c
Также напечатайте все числа, которые были сгенерированы, начиная с «переноса» (dup)-f
Найдите и напечатайте первое число за пределами цели, которое не было сгенерировано-s
Остановиться, если цель сгенерирована, даже если не все числа до того были сгенерированы-S
Как-s
и-f
в автоматическом цикле. Как только цель сгенерирована, найдите первый номер, который еще не сгенерирован, и сделайте его новой целью.-E
Не выходите сразу, когда цель достигнута. Сначала закончите все строки текущей длины-O
Не выводите строки для всех чисел вплоть до цели. просто строка для цели-o
Разрешенные инструкции (по умолчанию+-*/:
-b num
Самый низкий литерал, который можно сдвинуть (по умолчанию0
)-B num
Наибольший литерал, который может быть выдвинут (по умолчанию9
)-r num
Минимально допустимый промежуточный результат. Используется для избежания недостаточного заполнения. ( по умолчаниюINT32_MIN
,-2147483648
-R num
Максимально допустимый промежуточный результат. Используется, чтобы избежать переполнения. ( по умолчаниюINT32_MAX
,2147483647
-m memory
(только для Linux) выход, когда примерно столько дополнительной памяти выделеноНесколько интересных вариантов комбинаций:
Сгенерируйте все числа до цели и вычислите наименьшее число, для которого требуется более длинный генератор, чем все эти числа:
Генерация только цели (-s), печать только цели (-O)
Найдите наибольшее число, которое может быть сгенерировано в вашей системе с учетом времени и / или ограничений памяти (это приведет к тому, что вашей системе будет не хватать памяти, если вы оставите ее работающей. Вычтите 1 из последнего результата "поиска", который вы видите как последнее безопасное значение ):
Генерируйте решения без использования отрицательных промежуточных результатов (
30932
это первое значение, которое требует отрицательных промежуточных результатов для самой короткой строки):Генерируйте решения без каких-либо усилий
0
(кажется, это не приводит к каким-либо неоптимальным решениям):Генерация решений, в том числе
a..f (10..15)
:Генерируйте решения без использования dup
:
(добавьте,-r0
так как в этом случае отрицательные промежуточные значения никогда не будут интересны)Найти первое значение , которое не может быть создано для заданной длины строки , используя только
+
,-
,*
и/
:Фактически это сгенерирует первые несколько членов https://oeis.org/A181898 , но начнет расходиться,
14771
потому что мы используем усеченное деление, так что число может быть выполнено со строкой длиной 13 вместо длины 15 в качестве серии OEIS. ожидает:вместо того
Поскольку разделение без усечения кажется бессмысленным, ряд OEIS можно лучше генерировать с помощью
Предполагая, что разделение остается бесполезным, это дало мне 3 дополнительных условия, прежде чем мне не хватило памяти:
Другая версия этой программы, хранящая часть данных во внешних файлах, добавляет 135153107 и 675854293, после чего были сгенерированы все 32-битные целые числа.
befour.cpp
Некоторые тестовые случаи:
1: 1: 1
11: 3: 29+
26: 5: 29*8+
27: 3: 39*
100: 5: 19+:*
2431: 9: 56*9*9*1+
3727: 9: 69*7+:*6+
86387: 11: 67*:*1-7*7*
265729: 11: 39*:*:*2/9+
265620: 13: 99*::*6/*7+3*
1921600: 9: 77*:*:*3/
21523360: 9: 99*:*:*2/
57168721: 11: 99*6+:*8-:*
30932: 11: 159*-:4*:*+
источник
38950002
ваша программа дает89*7+:::**1-*
, что довольно хорошо, но вы можете сделать299*-::*:*+
для более коротких. Я думаю, что это подтверждаетint main(int argc, char const* const* argv)
Я не знаю C лучше, чем средний Джо, но что это? константный указатель на константный указатель на символ? Не должно бытьchar const *argv[]
или так (илиchar const **argv
если вы такой хардкор)?JavaScript узел грубой силы
Программный файл bfCodes.js
Работает под Windows
cmd.exe
cmd.exe
с помощью ярлыка и проверьте, что приглашение DOS начинается с рабочего каталогаоптимизация
Алгоритм циклически перебирает все комбинации символов befunge, начиная с строки кода длиной 1. Думайте о нем, как о вращении базового одометра 15 из наименее значащей цифры. Цифры высшего порядка перебираются с увеличением медленности.
bfCodes
не оценивает сгенерированный код, который сделал бы длину стека нулевой или отрицательной или оставил бы более одного числа в стеке в попытке оптимизировать скорость выполнения.Проблема грубой силы
Для кодового набора из 15 символов время, необходимое для прохождения всех комбинаций заданной длины, определяется как
это означает, что если ваша программа работает в пятнадцать раз быстрее, чем моя, вы сможете проверить только одну дополнительную строку символов в одно и то же время. Для одновременной проверки еще двух символов программа должна работать в 225 раз быстрее. Время, затрачиваемое при использовании метода грубой силы, увеличивается экспоненциально по мере увеличения длины строк кода. И величина числа обязательно указывает количество байтов, необходимых для его генерации.
Некоторые цифры.
Приблизительное время создания списков кодов в 32-битном блокноте Windows 7 для целых чисел до
Генерация befunge для 3727 (что составляет 66 в квадрате плюс 6) сама по себе заняла 1 час 47 минут и сгенерировала
578*+:*6+
Оптимальная генерация кода
Генерация befunge для чисел без проверки на самые короткие длины является относительно простой. Используя рекурсивный алгоритм, который использовал целочисленные квадратные корни и остатки, кодирование для чисел до 132 заняло около 3 мсек вместо 28 секунд. Они не были оптимальными. Из-за того, как он работал, этот конкретный алгоритм
638:*-:*+
генерировал для 3727 примерно 1 мсек (вместо часа или около того), что оказалось оптимальным.Проблема с предоставлением метода не грубой силы доказывает, что он оптимален в каждом случае. Удачи!
источник
+-*/
внутренними узлами0-9
и:
на листьях (и:
не может быть самой левой). Так что сгенерируйте и оцените все действительные деревья размером 2 * n + 1 на шаге n (n начинается с 0) и при необходимости преобразуйте их в строкиJavaScript
Что можно сделать с помощью фрагмента JS? На моей машине Firefox 64 бит, 416 за 60 секунд
источник