Скільки пар дужок достатньо для завершення Brainfuck Turing?


12

Brainfuck - це повна мова програмування, яка використовує лише 8 символів (6, якщо ви ігноруєте введення-виведення).

Дві найбільш помітні з них , які штовхають його на Тьюринга повнота [і ], по суті , етикетки і Гото Brainfuck в.

Зазвичай програми Brainfuck використовують кілька наборів [], але мені було цікаво, скільки саме пар цих дужок потрібно використовувати, щоб Brainfuck Turing завершився?

Простіше кажучи, яка найменша кількість дужок, що вам знадобиться для імітації n-стану машини Тюрінга (наведіть кількість дужок для 1, 2 та трьох державних машин Тюрінга)?

Примітки:

Ми припускаємо нескінченну стрічку і жодних обчислювальних обмежень.

Це 2-символьна машина Тьюрінга


1
"скільки пар цих дужок потрібно використовувати?" Чи можете ви уточнити "треба використовувати". Наприклад, що робити, якщо я попрошу BrainF порахувати до ? 21000000
Джон Л.

@ Apass.Як мінімальну кількість дужок
MilkyWay90

1
О, ти мав на увазі мінімальну кількість дужок для імітації -державної машини Тюрінга як функції ? У будь-якому випадку, ви можете надати нетривіальний приклад, який є максимально простим? nnn
Джон Л.

1
@ Apass.Jack Добре, я придумую програму BF, яка працює для
MilkyWay90

@ Apass.Jack Nevermind, це занадто важко для мене. В основному зробіть інтерпретатора BF для моєї мови програмування, машина Turing But But Worse , коли він використовує лише два можливі символи (0 і 1) і повністю видаляє аспект вводу / виводу та зупинки
MilkyWay90

Відповіді:


9

Це подальший розвиток відповіді @ ais523 , зведення її лише до двох наборів дужок, а також використання більш компактного розміщення комірок на основі теорії лінійки Голомба. ais523 зробив компілятор для цієї конструкції , а також цей TIO-сеанс, показуючи зразок результату програми BF, що працює з відстеженням налагодження лічильників TWM.

Як і оригінал, це починається з програми у моделі Водоспад , з деякими обмеженнями, які не втрачають загальності:

  1. Усі лічильники мають однакове значення : тобто карта тригера TWM має властивість для всіх .Rff(x,x)=Rx
  2. Є єдиний лічильник зупинки .h
  3. Число лічильників дорівнює для деякого простого числа .c(p1)/2p

Голомський правитель

Ми поєднуємо конструкцію Ердо-Турана з функцією перестановки масиву Вельха – Костаса , щоб отримати лінійку Голомба з необхідними властивостями.

(Я впевнений, що ця комбінована конструкція не може бути новою ідеєю, але ми просто знайшли і поєднали ці дві частини з Вікіпедії.)

Нехай - примітивний корінь . Визначте функціюrp=2c+1

g(k)=4ck((rk1)mod(2c+1)),k=0,,2c1.

  1. g - лінійка Голоба порядку . Тобто різниця є унікальною для кожної пари різних чисел .2 c g ( i ) - g ( j ) i , j { 0 , , 2 c - 1 }2cg(i)g(j)i,j{0,,2c1}
  2. g(k)mod(2c) приймає кожне значення рівно один раз.0,,2c1

Структура стрічки

Для кожного TWM лічильника ми призначаємо два позиції комірки стрічки BF, резервну комірку та комірку значення :x{0,,c1}u ( x ) v ( x ) u(x) v(x)

u(x)=g(k1)<v(x)=g(k2) with u(x)v(x)x(modc)

За другою властивістю є рівно два різних значення на вибір.gk1,k2

Вміст резервної комірки буде зберігатися у більшості , за винятком випадків, коли його лічильник щойно відвідав, коли він буде , удвічі більший за значенням самостійного скидання лічильника. Осередок значень зберігатиметься удвічі більше, ніж відповідний лічильник TWM.02R

Усі інші комірки, до яких можна отримати виконання програми BF (кінцеве число), зберігатимуться з непарними значеннями, так що вони завжди перевіряються як ненульові. Після ініціалізації це автоматично, оскільки всі налаштування комірок є парними.

При бажанні всі позиції комірок можна зміщувати вправо на постійну, щоб уникнути переміщення вліво від початкового положення стрічки BF.

Структура програми BF

Нехай - відстань між значенням лічильника зупинки і резервними осередками, і - число, достатньо велике, що для всіх лічильників . Тоді основна структура програми BFH=v(h)u(h)NcN+1v((x+1)modc)u(x)x

ініціалізація коригування[ >×(H+cN+1) [ <×c ] <×H ]

Ініціалізація

Ініціалізації фази встановлює всі клітини досяжні програмою їх початкові значення, в стані , як ніби тільки що відвідав останній лічильник і тільки що активна осередок була її резервної осередку :u(c1)

  1. Осередки значень ініціалізуються вдвічі від початкового вмісту відповідного лічильника TWM, за винятком того, що лічильник попередньо зменшений.0
  2. Осередки запасного звороту встановлюються на , за винятком комірки , для якої встановлено .0u(c1)2R
  3. Усі інші комірки, доступні програмою (кінцеве число), встановлені в .1

Потім вказівник стрічки переміщується у положення (завжди ненульова комірка), перш ніж ми дістаємось до програми першої .u(c1)H[

Початок зовнішньої петлі

На початку ітерації зовнішньої петлі покажчик стрічки буде для лічильника або або .u(x)Hv(x)Hx

Нехай є наступним лічильником для відвідування.y=((x+1)modc)

Рух розміщує вказівник стрічки на положення, яке є а не зліва від .>×(H+cN+1)y(modc)v(y)

Внутрішня петля тепер шукає ліворуч по кроках для нульової комірки. Якщо лічильник дорівнює нулю, то він зупиниться на комірці (нуль) значення ; інакше вона знайде запасну комірку .[ <×c ]cyv(y)u(y)

Незалежно від того, яка клітина знайдена, стає новою активною клітиною.

Коригування

Регулювання фази регулює різні клітини на стрічці на основі їх становища щодо активної комірки. Цей розділ містить лише +-><команди, і ці коригування відбуваються беззастережно. Однак, оскільки всі контр-споріднені комірки є у шаблоні лінійки Голомба, будь-які коригування, невідповідні поточній активній комірці, пропустять усі важливі комірки і замість цього скоригують якусь нерелевантну клітинку (зберігаючи її непарною).

Таким чином, окремий код повинен бути включений у програму для кожної можливої ​​пари активної та відрегульованої комірки, за винятком самонастроювання активної комірки, яка, оскільки регулювання базується виключно на відносному положенні, повинна розподілятися між усіма ними.

Необхідні коригування:

  1. Відрегулюйте резервну комірку попереднього лічильника на .u(x)2R
  2. Відрегулюйте резервну комірку поточного лічильника на , за винятком випадків, коли поточна активна комірка і тому ми повинні зупинитися.u(y)2Rv(h)
  3. Відрегулюйте комірку значення наступного лічильника на (зменшення лічильника).v((y+1)modc)2
  4. Коли активна комірка є осередком величини (щоб лічильник досяг нуля), відрегулюйте всі комірки значення на з тригерної карти TWM. сама коригується на .v(y)yv(z)2f(y,z)v(y)2R

Перше і друге коригування, викладені вище, обумовлені тим, що всі активні комірки повинні налаштувати себе на одне значення, що становить для осередкових значень, а отже, і для резервних комірок. Для цього потрібна підготовка та очищення резервних комірок, щоб вони повернулися до як у галузях значень, так і в резервних.2R0

Кінець зовнішньої петлі

Рух означає, що в кінці етапу коригування покажчик стрічки переміщується на місцями зліва від активної комірки.<×HH

Для всіх активних клітин інших , ніж значення осередки Зупинки лічильника , це не має значення осередку, і так непарній і не одно нулю, і зовнішній цикл триває в протягом наступної ітерації.v(h)

Для вказівник замість цього розміщується на відповідній клітині запасного , для якої ми зробили виняток вище, щоб утримати його нульовим, і тому програма виходить через фінал та зупинки.v(h)u(h)]


12

Я не на 100% впевнений, що це неможливо зробити за допомогою двох комплектів дужок. Однак, якщо комірки стрічки BF дозволяють без обмежених значень, достатньо трьох наборів дужок. (Для простоти я також припускаю, що ми можемо перемістити головку стрічки ліворуч від її початкової точки, хоча, оскільки ця конструкція використовує лише кінцеву область стрічки, ми могли б зняти це обмеження, додавши достатньо багато >команд на початку програма.) Конструкція нижче вимагає припущення Артінавміти складати довільно великі програми; однак, навіть якщо думка Артіна помилкова, все одно можна буде показати Повноту Тюрінга побічно, переклавши інтерпретатора мови Тюрінга на BF, використовуючи конструкцію внизу, та запустивши довільні програми, подавши їх як вхід до цього перекладача.

Мова Тюрінга, яку ми складаємо в безмежну BF, - це модель водоспаду , яка є однією з найпростіших відомих обчислювальних моделей. Для людей, які цього ще не знають, він складається з декількох лічильників (і початкових значень для них) та функції від пар лічильників до цілих чисел; Виконання програми складається з багаторазового віднімання 1 з кожного лічильника, тоді якщо будь-який лічильник дорівнює 0, додавання до кожного лічильника (програма записується таким чином, що це ніколи не відбувається з кількома лічильниками одночасно). За моїм посиланням є доказ повноти Тьюрінга для цієї мови. Не втрачаючи загальності, ми будемо вважати, що всі лічильники мають однакове значення самозавантаження (тобтоfxf(x,y)yf(x,x) однаковий для всіх ); це безпечне припущення, оскільки для будь-якого конкретного додавання однакової константи до кожного не змінить поведінку програми.xxf(x,y)

Нехай - кількість лічильників; не втрачаючи загальності (припускаючи гіпотезу Артіна), припустимо, що має примітивний корінь 2. Нехай буде , де - найменша потужність на 2, більша за . Без втрати загальності буде менше ( обмежено поліноміально, зростає експоненціально, тому будь-який достатньо великий буде працювати).ppqp(1+s+s2)sp2q2p2q2pp

Розташування стрічки полягає в наступному: пронумеруємо кожен лічильник цілим числом (і без втрати загальності вважаємо, що існує один лічильник зупинки і пронумеруємо його ). Значення більшості лічильників зберігається на комірці стрічки , за винятком лічильника 0, який зберігається на клітинці стрічки . Для кожної комірки з непарними номерами стрічки від клітинки -1 до та включає0i<p22i2q2p+1+2p+1, ця клітинка стрічки завжди містить 1, крім випадків, коли вона знаходиться ліворуч від лічильника; у цьому випадку вона завжди містить 0. Яєчкові клітинки стрічки, які не використовуються як лічильники, мають невідповідні значення (які можуть бути, а можуть і не бути 0 ); і комірки стрічкового номера з непарними номерами поза вказаним діапазоном також мають неактуальні значення. Зауважте, що для встановлення стрічки у відповідний початковий стан потрібно ініціалізувати лише кінцево багато елементів стрічки до постійних значень, це означає, що ми можемо це зробити з послідовністю <>+-інструкцій (насправді >+вони потрібні лише ), тому дужки не мають. В кінці цієї ініціалізації переміщуємо покажчик стрічки на комірку -1.

Загальна форма нашої програми буде виглядати приблизно так:

ініціалізація коригування[>>>[ >×(2p1) [ <×(2p) ]>-] <<<]

Ініціалізація ставить стрічку в очікувану форму, а вказівник на клітинку -1. Це не клітинка зліва від лічильника (0 не є потужністю 2), тому вона має значення 1, і ми вводимо цикл. Інваріант циклу для цієї зовнішньої петлі полягає в тому, що вказівник стрічки (на початку та в кінці кожної ітерації циклу) три комірки зліва від лічильника; видно, що цикл вийде таким чином, лише якщо у нас три комірки зліва від лічильника 2 (у кожного лічильника є 1 три комірки ліворуч, а щоб мати 0, це означатиме, що позиції стрічки двох лічильників були 2 комірки один від одного; єдині дві потужності 2, які відрізняються на 2, є і , і бінарне представлення змінюється від рядків s до рядків2122q01s або навпаки, щонайменше чотири рази, і, отже, не можна бути віддаленим від потужності 2).

Друга петля багаторазово петлюється над лічильниками, зменшуючи їх. Інваріант циклу полягає в тому, що вказівник стрічки завжди вказує на лічильник; таким чином цикл вийде, коли деякий лічильник стане 0. Декремент справедливий -; шлях, який ми переходимо від одного лічильника до іншого, є складнішим. Основна ідея полягає в тому, що переміщення пробілів праворуч від розмістить нас на осередку з непарною нумерацією , що знаходиться праворуч від будь-якого лічильника ( - останній лічильник, - позитивний, оскільки - позитивний); за модулем це значення відповідає (за малою теоремою Ферма) . Найпотужніша петля кілька разів переміщується вліво2p12x2p+2x12p2x1x2p2x+12p пробіли, які також не змінюють індексу модуля комірки стрічки , і, зрештою, повинні знайти комірку конгруентною до модуля який має значення (яке буде клітинка зліва від деякого лічильника); через нашу первісну кореневу вимогу існує саме одна така клітинка ( є конгруентною до модуля , а відповідає для будь-якої інше , де - дискретний логарифм до основи 2 по модулю ). Додатково видно, що положення вказівника стрічки модуль2p2x+12p2q112p2log2,p(r)+112r1rlog2,pp2pзбільшується на рази кожен раз, округлюючи середню петлю. Таким чином, покажчик стрічки повинен переходити між усіма лічильниками (у порядку їх значень по модулю ). Таким чином, з кожними ітераціями ми зменшуємо кожен лічильник (як потрібно). Якщо цикл проривається частково через ітерацію, ми відновимо зменшення, коли повторно введемо цикл (оскільки решта зовнішньої петлі не змінює мережу в положення вказівника стрічки).2p2pp

Коли лічильник набиває 0, середній цикл розривається, переводячи нас до коду "коригування". Це в основному просто кодування ; для кожної пари він додає до елемента стрічки, який знаходиться на тій же відстані ліворуч / праворуч від поточного вказівника стрічки, що і розташування стрічки лічильника ліворуч / праворуч від лічильника 's розташування стрічки (а потім видаляє вказівник стрічки туди, де вона почалася). Щоразу, коли , ця відстань виявляється унікальною:f(x,y)f(x,y)yxxy

  • Різниця між двома потужностями 2 - це двійкове число, що складається з рядка 1 або більше с, за яким слідує рядок 0 або більше с (зі значеннями місця початку початку числа і початку рядка , залежно від більших і менших відповідно і ); тому всі ці відмінності є чіткими. * Що стосується різниці потужності 2 і , вона повинна містити щонайменше два переходи між рядками с і с (100xyq10q містить щонайменше чотири таких переходи, віднімання може видалити лише 2), таким чином відрізняється від усіх відмінностей двох степенів двох, і ці відмінності, очевидно, також відрізняються один від одного.

Для ми, очевидно, знаходимо, що відстань, яку перемістили, дорівнює 0. Але оскільки всі рівні, ми можемо просто використовувати це як коригування для поточної комірки. І видно, що код коригування таким чином реалізує ефект "коли лічильник б'є 0" для кожного лічильника; всі комірки, які насправді представляють лічильники, будуть скориговані на правильну кількість, а всі інші коригування впливатимуть на безлічинні парні комірки (різниця між двома парними числами є парними), які не впливають на поведінку програми.x=yf(x,y)

Таким чином, тепер ми маємо робочу компіляцію будь-якої програми в моделі Водоспад до BF (включаючи поведінку зупинки, але не включаючи введення-виведення, яке не потрібно для повноти Тьюрінга), використовуючи лише три пари дужок, і, отже, три пари дужок достатньо для Тюрінга-повноти.


Хороша робота! Я бачу, ви працювали над цим у ТНБ!
MilkyWay90

Я думаю, вам потрібно, щоб принаймні p + 2. Коли s = p + 1, q на 1 менше, ніж потужність 2.
Ørjan Johansen

Я думаю , що я знайшов набагато простіше (як не вимагає прем'єр теорії чисел) розміщення лічильника: 2p*2^i+2i.
Ørjan Johansen

@ ØrjanJohansen: Так, я думаю, я згадував про це будівництво в #esoteric (через деякий час після того, як я написав це повідомлення)? Все, що вам потрібно, - це лінійка Golomb, для якої кожен елемент відрізняє по модулю кількість елементів, і існують різні способи їх побудови (хоча знайти оптимальні - важко; найдовший, що я знайшов (через грубу силу) - це [0, 1, 3, 7, 20, 32, 42, 53, 58]p = 9).
ais523

О, так ви зробили (якраз перед тим, як я сказав, що мій мозок відмовився перебувати в математичному режимі, тож є моє виправдання: P). Я думаю, я виявив, що k = 0 було достатньо. Я думаю, що Erd Wikis-Turan_construction Wikipedia дає поліноміально зростаючий (і, мабуть, O () - оптимальний?), Якщо ви використовуєте лише першу половину елементів (інша половина повторюється (mod p)).
Ørjan Johansen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.