Перерахуйте дійсні програми 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

Пітон 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 та Mitch Schwartz: D

Як це працює:

Це відображає всі дійсні програми BF на всі можливі, дійсні або недійсні програми BF, які не починаються з a [, у співвідношенні один до одного. Після цього нова програма просто перетворюється на восьмигранну.

Ось формула відображення:

  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 Чи пуста програма вважається програмою?
Бета-розпад


@Dennis Я це виправлю, коли пограю.
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

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)є біективної функцією ранжування, про яку йдеться.

Основна ідея полягає в тому, що непуста програма може починатись [із одного із шести []символів або з них . У першому випадку ми можемо переглядати можливі місця узгодження ]та повторювати на доданій частині та на хвості (де хвіст означає підрядку, що слідує за ]). В останньому випадку ми можемо повторювати хвіст (де хвіст означає, що випадає перший символ). Це міркування можна використовувати як для підрахунку, так і для обчислення рангів.

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

Наприклад, у 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

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