Перечислите действительные программы Brainf ** k

41

Golunar / Одинарный способ кодирования всех действительных Brainfuck программ, но это не перечисление, так как большинство натуральных чисел не соответствуют действительной программе.

Для этой задачи предположим, что лента бесконечно вдвойне бесконечна и без комментариев, т. Е. Программа Brainfuck действительна тогда и только тогда, когда она состоит только из символов <>+-.,[]и все левые и правые скобки совпадают.

Например, пустая программа ,[+][-].,[>+<[--].] и +[+[+][+[+]+]+]+.является действительной программой Brainfuck, в то время как ][, и a[]нет.

задача

Напишите программу или функцию, которая принимает действительную программу Brainfuck в качестве входных данных и возвращает натуральное число ( 1 , 2 , 3 ,…) со следующими ограничениями:

  • Сгенерированный вывод должен быть разным для всех действительных программ Brainfuck.

  • Для каждого натурального числа n должна существовать действительная программа Brainfuck, которая, если она предоставляется в качестве входных данных, генерирует выходные данные n .

Дополнительные правила

  • Если программа Brainfuck имеет длину 100 или менее байтов, ваша программа или функция должны завершиться в течение одной минуты.

    Это означает, что вы не можете перебирать все действительные программы Brainfuck до тех пор, пока не сопоставите ввод.

  • Применяются стандартные правила .

Деннис
источник
3
Я думал просто закодировать это как восьмеричное, но соответствующие скобки делают это сложно.
DankMemes
Является ли пустая программа допустимой программой Brainfuck? Должно ли оно быть отображено на натуральное целое число?
orlp
9
Почему закрытое голосование? Это увлекательный вопрос, и проблема в том, чтобы иметь размер и разнообразие для отличного гольфа.
трихоплакс
1
@orlp Да, пустая программа удовлетворяет вышеуказанному определению
Деннис
3
Все еще жду ответа, написанного на Brainfuck ...
Майкл Хэмптон

Ответы:

16

Python 3, 443 158 155 154 134 131 128 124 117 116 115 байтов

c=d=C=D=0
for e in input():v='[<>,.-+]'.find(e);d=d*8+v;c+=c<0<6<v;c-=d>1>v;C,D=(c,C+1,d,D)[v>6::2]
print(-~D*8**C)

Несколько байтов благодаря Sp3000 и Митчу Шварцу: D

Как это работает:

Это отображает все действительные программы BF на все возможные, действительные или недействительные программы BF, которые не начинаются с [ , в соотношении один к одному. После этого новая программа просто преобразуется в восьмеричное.

Вот формула сопоставления:

  1. Разделите программу BF на 3 части. Первая часть - это самый большой префикс, состоящий только из [символов. Третья часть - самый большой постфикс, состоящий только из ]символов. Вторая часть - это середина.
  2. Утилизируйте первую часть. Они могут быть пересчитаны позже.
  3. Снимите все ]скобки в третьей части, которые совпадают со [скобками во второй части. Они также могут быть пересчитаны позже.
  4. Соедините вторую и третью части вместе.

Если вы не понимаете это объяснение, вы можете найти расширенное объяснение в чате, начиная здесь .

Для справки, вот первые 20 программ:

1 : 
2 : <
3 : >
4 : ,
5 : .
6 : -
7 : +
8 : []
9 : <[]
10 : <<
11 : <>
12 : <,
13 : <.
14 : <-
15 : <+
16 : [<]
17 : >[]
18 : ><
19 : >>
20 : >,

Вот первые 1000 программ: http://pastebin.com/qykBWhmD
Вот программа, которую я использовал для их создания: http://ideone.com/e8oTVl

Вот Hello, World!:

>>> ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
457711481836430915510337664562435564418569135809989841510260388418118348571803953323858180392373
Номер один
источник
Незначительный спор: вы не можете отобразить программу на 0 .
Деннис
@Dennis Пустая программа считается программой, хотя?
Бета-распад
@BetaDecay Да: codegolf.stackexchange.com/questions/55363/…
TheNumberOne
@ Денис, я исправлю это, когда буду играть в гольф.
TheNumberOne
3
Это гениально
гордый haskeller
13

Python 2, 157 байт

def f(s,o=0,d=0,D={}):T=s,o,d;x=D[T]=D[T]if T in D else~o and 0**o+sum(f(s[1:],cmp(c,"[")%-3-~o,d or cmp(c,s[0]))for c in"+,-.<>[]")if s else~d<0==o;return+x

По-прежнему выглядит неплохо для игры в гольф, но я публикую это сейчас. Он использует рекурсию с небольшим кэшированием. Досадно,D.get что для кеширования не происходит короткое замыкание, поэтому я не могу сохранить 9 байтов таким образом ...

Сопоставление сначала определяет длину, затем лексикографический порядок над порядком "][><.-,+"(см. Примеры вывода ниже). Основная идея - сравнить префиксы.

Переменная oотслеживает количество [открытых скобок для текущего префикса, в то время как переменная dпринимает одно из трех значений, указывающих:

  • d = 1: Текущий префикс лексикографически раньше, чем s. Добавьте все программы с этим префиксом и длиной <= s,
  • d = -1: Текущий префикс лексикографически позже, чем s. Добавьте все программы с этим префиксом и длиной < s.
  • d = 0: Текущий префикс является префиксом s, поэтому мы можем изменить его dна 1 или -1 позже.

Например, если у нас есть s = "[-]"и наш текущий префикс p = "+", так как pэто позже, чем sлексикографически, мы знаем только добавить программы, начиная с pкоторых строго короче, чем s.

Чтобы дать более подробный пример, предположим, что у нас есть программа ввода s = "-[]". Первое рекурсивное расширение делает это:

  (o == 0)               # Adds a program shorter than s if it's valid
                         # For the first expansion, this is 1 for the empty program
+ f(s[1:], o=-1, d=1)    # ']', o goes down by one due to closing bracket
+ f(s[1:], o=1, d=1)     # '[', o goes up by one due to opening bracket
+ f(s[1:], o=0, d=1)     # '>'
+ f(s[1:], o=0, d=1)     # '<'
+ f(s[1:], o=0, d=1)     # '.', d is set to 1 for this and the previous branches
                         # since they are lexicographically earlier than s's first char
+ f(s[1:], o=0, d=0)     # '-', d is still 0 since this is equal to s's first char
+ f(s[1:], o=0, d=-1)    # ',', d is set to -1 for this and the later branches
                         # since they are lexicographically later than s's first char
+ f(s[1:], o=0, d=-1)    # '+'

Обратите внимание , как мы на самом деле не использовать префиксы в рекурсии - все , что мы заботимся о них захватывается через переменные d, oи сокращение ввода программы s. Вы заметите много повторений выше - именно здесь происходит кэширование, позволяющее нам обрабатывать программы из 100 символов в срок.

Когда sпусто, мы смотрим (d>=0 and o==0), который решает, возвращать ли 1 (считать эту программу, потому что она лексикографически ранняя / равная и программа действительна), или 0 (не считать эту программу).

Любая ситуация с o < 0немедленным возвратом 0, так как любые программы с этим префиксом имеют больше] s, чем [и, следовательно, являются недействительными


Первые 20 выходов:

 1
> 2
< 3
. 4
- 5
, 6
+ 7
[] 8
>> 9
>< 10
>. 11
>- 12
>, 13
>+ 14
<> 15
<< 16
<. 17
<- 18
<, 19
<+ 20

Используя тот же пример Hello World, что и ответ @ TheNumberOne:

>>> f("++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.")
3465145076881283052460228065290888888678172704871007535700516169748342312215139431629577335423L
Sp3000
источник
4

Python 2, 505 (не в гольф)

Мне нравилось разрабатывать этот подход, но я могу не беспокоить его, потому что он не конкурентоспособен по сравнению с другими подходами. Я публикую это для разнообразия и возможного эстетического интереса. Это включает в себя рекурсию и немного математики.

F={0:1}

def f(n):
    if n not in F:
        F[n]=6*f(n-1) + sum(f(i)*f(n-2-i) for i in range(n-1))

    return F[n]

def h(x):
    if x=='': return 0

    if len(x)==1: return '+-<>,.'.find(x)

    if x[0]!='[':
        return h(x[0]) * f(len(x)-1) + h(x[1:])

    d=i=1
    while d:
        if x[i]==']': d-=1
        elif x[i]=='[': d+=1
        i+=1

    a=i-2
    b=len(x)-i

    return 6*f(a+b+1) + sum(f(i)*f(a+b-i) for i in range(a)) + h(x[1:i-1]) * f(b) + h(x[i:])

def g(x):
    return sum(f(i) for i in range(len(x))) + h(x) + 1

print g(raw_input())

Функция f(n)подсчитывает количество действительных программ длины мозга n. h(x)карты программа длины nдо [0..f(n)-1], и g(x)это биективная функция ранжирования в вопросе.

Основная идея заключается в том, что непустая программа может запускаться [либо с одним из 6 не []символов. В первом случае мы можем перебирать возможные местоположения сопоставления ]и повторяться на закрытой части и на хвосте (где хвост означает подстроку, следующую за] ). В последнем случае мы можем выполнить рекурс на хвосте (где хвост означает выпадение первого символа). Это рассуждение можно использовать как для подсчета, так и для вычисления ранга.

Более короткие программы всегда будут иметь более низкий рейтинг, чем более длинные программы, и шаблон скобок является второстепенным определяющим фактором. Не- []символы сортируются в соответствии с "+ - <>,". (что произвольно).

Например, у n=4нас есть эти случаи:

zxxx
[]xx
[x]x
[xx]

где zобозначает не []символьный и xобозначает любой символ, при условии, что ]он должен совпадать с начальным [. Программы ранжируются в соответствии с этим порядком и рекурсивно по xподразделам, причем левый раздел имеет приоритет над правым разделом в последних случаях. Расчет ранга аналогичен системам счисления со смешанным основанием и fважен для вычисления текущего «основа».

Митч Шварц
источник
4

Этот ответ является формальным доказательством ответа TheNumberOne , перечисления действительных программ Brainf ** k , где может быть немного трудно понять тонкости, почему перечисление является правильным. Нетрудно понять, почему не существует какой-либо недопустимой программы, которая отображается на число, которое не включено в допустимую программу.

В этом ответе заглавные буквы используются для обозначения программ, а строчные переменные используются для функций и целых чисел. ~ является оператором конкатенации.

Предложение 1:

Пусть функция f будет программой, описанной в этом ответе. Тогда для каждой программы U существует действительная программа V такая, что f (U) = f (V)

Определение 1:

Пусть g (X) будет номером того, [что появляется в программе X, и пусть h (X) будет номером того, ]что появляется.

Определение 2:

Определите P (x), чтобы быть этой функцией:

P(x) = "" (the empty program) when x <= 0
P(x) = "]" when x = 1
P(x) = "]]" when x = 2
etcetera

Определение 3:

Для программы X обозначим X1 как самый большой префикс [символов, X2 - его центр, а X3 - самый большой суффикс ]символов.

Доказательство предложения 1:

Если g (U) = h (U), то U - правильная программа, и мы можем взять V = U. (тривиальный случай).

Если g (U) <h (U), то мы можем создать V, добавив n = h (U) - g (U) [символов. Очевидно, что f (V) = f (U), поскольку все [символы в префиксе удалены.

Теперь рассмотрим g (U)> h (U). Определить T = U2 ~ U3. если g (T) <= h (T), то мы можем построить V, удалив n = g (U) - h (U) [символов.

Таким образом, мы можем предположить, что h (T) <g (T). Построим V = T ~ P (g (T) - h (T)).

Нам нужно три небольших факта, чтобы продолжить:

Утверждение 1: g (U2) = g (T)

U3 [по своему определению не содержит символов. Поскольку T = U2 ~ U3, все его [символы находятся в первой части.

Утверждение 2: h (U3) <g (T)

Это следует из того, что h (T) <g (T) и h (U3) <h (U3 ~ U2) = h (T).

Утверждение 3: h (V3) = g (U2) - h (U2)

h(V3) = h(U3) + g(T) - h(T)                           using the construction of V
h(V3) = h(U3) + g(U2) + g(U3) - h(U2) - h(U3)         apply the definition of T
h(V3) = g(U2) - h(U2) *one term cancels, g(U3)        is always zero, as U3 contains only `]` symbols*

Теперь покажем, что f (V) = f (U).

f(U) = U2 ~ P(h(U3) - g(U2)) = U2                     claim 2, definition of P

f(V) = U2 ~ P(h(V3) - g(V2))
     = U2 ~ P(h(V3) - g(U2))
     = U2 ~ P(g(U2) - h(U2) - g(U2))                  claim 3
     = U2 ~ P(-h(U2))
     = U2                                             definition P

Это завершает доказательство. QED

Давайте сделаем уникальность также.

Предложение 2:

Пусть U, V две разные, действительные программы. Тогда f (U)! = F (V)

Это довольно просто по сравнению с предыдущим предложением.

Предположим, что U2 = V2. Но тогда единственный способ, которым U и V могут различаться, - это добавление или удаление n [и ]символов к U1 и U3 соответственно. Тем не менее, это изменяет вывод f, так как f будет подсчитывать количество непревзойденных ]символов в суффиксе.

Таким образом, U2! = V2.

Очевидно, это приводит к противоречию. Поскольку U2 и V2 буквально содержатся в выходных данных f (U) и f (V) соответственно, они не могут отличаться, кроме как на «краю», месте, где U2 соединяется с U3. Но первый и последний символы U2 и V2 не могут быть [или ]по определению, в то время как это единственные символы, разрешенные в U1, U3, V1, V3 соответственно и соответственно снова. Таким образом, мы получаем U2 = V2. QED

тля
источник