Найшвидший Mini-Flak Quine


26

Міні-Flak є підмножиною Brain-Flak мови, де <>, <...>і []операції невирішеним. Строго кажучи, він не повинен відповідати такому регексу:

.*(<|>|\[])

Mini-Flak - це найменший з відомих повних підмножин Тьюрінга Brain-Flak.


Нещодавно мені вдалося зробити Quine в Mini-Flak , але це було занадто повільно, щоб бігти протягом життя Всесвіту.

Тож мій виклик для вас - зробити швидше Quine.


Оцінка балів

Щоб оцінити свій код, поставте @cyпрапор в кінці коду і запустіть його в інтерпретаторі Ruby ( Спробуйте в Інтернеті, використовуючи інтерпретатор рубіну), використовуючи -dпрапор. Ваша оцінка повинна надрукуватися в STDERR наступним чином:

@cy <score>

Це кількість циклів, які проходить ваша програма перед завершенням, і однакове між запусками. Оскільки кожен цикл займає приблизно однакову кількість часу для запуску, ваш рахунок повинен бути безпосередньо пов'язаний з часом, необхідним для запуску вашої програми.

Якщо Quine занадто довгий, щоб ви розумно працювали на своєму комп’ютері, ви можете обчислити кількість циклів вручну.

Розрахувати кількість циклів не дуже складно. Кількість циклів еквівалентна 2-кратній кількості пробіг монад плюс кількості пробіжних молюсків. Це те саме, що замінити кожний nilad одним символом і підрахувати кількість запущених символів.

Приклад балів

  • (()()()) набрав 5 балів, оскільки він має 1 монаду та 3 нилади.

  • (()()()){({}[()])} Оцінка 29, тому що перша частина така ж, як і раніше, і 5, тоді як цикл містить 6 монад і 2 нила, які оцінюють 8. Цикл виконується 3 рази, тому ми рахуємо його бал 3 рази. 1*5 + 3*8 = 29


Вимоги

Ваша програма повинна ...

  • Майте принаймні 2 байти

  • Роздрукуйте його вихідний код при виконанні в Brain-Flak, використовуючи -Aпрапор

  • Не відповідає регексу .*(<|>|\[])


Поради

  • Кран-Flak перекладач категорично швидше , ніж інтерпретатор рубіна , але не вистачає деяких функцій. Я рекомендую спершу протестувати ваш код за допомогою Crane-Flak, а потім зарахувати його в інтерпретаторі рубіну, коли ви знаєте, що він працює. Я також дуже рекомендую не запускати свою програму в TIO. TIO не тільки повільніше, ніж інтерфейс для настільних перекладачів, але ще й час очікує приблизно за хвилину. Було б надзвичайно вражаюче, якби комусь вдалося отримати досить низький рівень, щоб запустити свою програму до того, як TIO вичерпався.

  • [(...)]{}і (...)[{}]працювати так само, як, <...>але не порушувати вимогу з обмеженим джерелом

  • Ви можете перевірити Brain-Flak та Mini-Flak Quines, якщо хочете уявити, як вирішити цю проблему.


1
"current best" -> "only current"
HyperNeutrino

Відповіді:


33

Mini-Flak, 6851113 циклів

Програма (буквально)

Я знаю, що більшість людей, ймовірно, не очікують, що у Quine Mini-Flak використовується недруковані символи та навіть багатобайтові символи (що робить кодування релевантним). Тим не менш, ця quine робить, і недруковані файли, у поєднанні з розміром quine (93919 символів, закодованих як 102646 байт UTF-8), ускладнюють розміщення програми на цій посаді.

Однак програма дуже повторювана, і як така, стискає дійсно добре. Таким чином, що вся програма доступна буквально з Stack Exchange, є xxdзворотний шестигранник gzipкомпресованої версії повної quine, прихованої за розбірним нижче:

(Так, це настільки повторюється, що ви навіть можете побачити повтори після його стискання).

Питання говорить: "Я також дуже рекомендую не запускати свою програму в TIO. Мало того, що TIO повільніше, ніж настільний інтерпретатор, але також буде таймаут приблизно через хвилину. Було б надзвичайно вражаюче, якби комусь вдалося набрати достатньо низький рівень для запуску їх програма до вичерпання часу TIO. " Я можу зробити це! На TIO потрібно близько 20 секунд, використовуючи інтерпретатор Ruby: Спробуйте в Інтернеті!

Програма (читається)

Тепер я дав версію програми, яку можуть читати комп’ютери, давайте спробуємо версію, яку можуть прочитати люди. Я перетворив байти, що складають квину, у кодову сторінку 437 (якщо вони мають високий набір бітів) або контрольні зображення Unicode (якщо вони керують кодами ASCII), додав пробіл (будь-який попередній пробіл був перетворений на контрольні зображення ), кодується за допомогою довжини за допомогою синтаксису «string×length», а деякі біти, важкі для даних:

␠
(((()()()()){}))
{{}
    (({})[(()()()())])
    (({})(
        {{}{}((()[()]))}{}
        (((((((({})){}){}{})){}{}){}){}())
        {
            ({}(
                (␀␀!S␠su! … many more comment characters … oq␝qoqoq)
                («()×35» («()×44» («()×44» («()×44» («()×44» («()×45»
                … much more data encoded the same way …
                («()×117»(«()×115»(«()×117»
                «000010101011┬â┬ … many more comment characters … ┬â0┬â┬à00␈␈
                )[({})(
                    ([({})]({}{}))
                    {
                        ((()[()]))
                    }{}
                    {
                        {
                            ({}(((({}())[()])))[{}()])
                        }{}
                        (({}))
                        ((()[()]))
                    }{}
                )]{}
                %Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'×almost 241»
                ,444454545455┬ç┬ … many more comment characters … -a--┬ü␡┬ü-a␡┬ü
            )[{}()])
        }{}
        {}({}())
    )[{}])
    (({})(()()()()){})
}{}{}␊

("Практично 241" - це те, що в 241-й копії відсутня остання ', але в іншому випадку ідентична іншим 240.)

Пояснення

Про коментарі

Перше, що потрібно пояснити, що з недрукованими символами та іншими мотлохами, що не є командами Mini-Flak? Ви можете подумати, що додавання коментарів до quine просто ускладнює справи, але це конкуренція за швидкість (а не конкуренція за розмірами), тобто коментарі не шкодять швидкості програми. Тим часом Brain-Flak, а отже, і Mini-Flak, просто скидають вміст стеку до стандартного виводу; якби вам довелося переконатися, що стек містив тількисимволи, які складали команди вашої програми, вам доведеться проводити цикли, чистячи стек. Наразі, Brain-Flak ігнорує більшість символів, тому якщо ми гарантуємо, що елементи стек-файлів не є дійсними командами Brain-Flak (що робить це поліглотом Brain-Flak / Mini-Flak) і не є негативними або зовні Діапазон Unicode, ми можемо просто залишити їх у стеку, дозволити їх виведення та поставити той самий символ у нашій програмі на те саме місце, щоб зберегти властивість quine.

Є один особливо важливий спосіб, яким ми можемо скористатися цим. Quine працює за допомогою довгих рядків даних, і в основному весь вихід з quine виробляється шляхом форматування рядка даних різними способами. Існує лише один рядок даних, незважаючи на те, що програма має кілька фрагментів; тому нам потрібно мати можливість використовувати один і той же рядок даних для друку різних частин програми. Трюк "непотрібні дані не мають значення" дозволяє нам це зробити дуже простим способом; ми зберігаємо символи, що складають програму, у рядок даних, додаючи або віднімаючи значення до коду ASCII або з нього. Зокрема, символи, що становлять початок програми, зберігаються як їх ASCII код + 4, символи, що складають розділ, який повторюється майже 241 раз як їх ASCII код - 4,кожен символ рядка даних зі зміщенням; якщо, наприклад, ми друкуємо його з 4-ма доданими до кожного коду символів, ми отримуємо одне повторення повторного розділу з деякими коментарями до і після. (Ці коментарі - це просто інші розділи програми. Коди символів зміщені так, що вони не утворюють дійсних команд Brain-Flak, оскільки було додано неправильне зміщення. Ми повинні ухилятися від команд Brain-Flak, а не лише Mini- Команди Flak, щоб уникнути порушення частини запитання з ; вибір зсувів був покликаний забезпечити це.)

Через цей фокус з коментарями нам насправді потрібно лише вміти виводити рядок даних, відформатований двома різними способами: а) кодується так само, як у джерелі; б) як символьні коди із заданим зміщенням, що додаються до кожного коду. Це величезне спрощення, що робить додаткову довжину цілком вартим.

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

Ця програма складається з чотирьох частин: вступ, рядок даних, формат даних рядків даних та вихідний текст. Вступ та outro в основному відповідають за запуск рядка даних та його форматера в циклі, щоразу вказуючи відповідний формат (тобто, кодувати чи зсув, і який зсув використовувати). Рядок даних - це лише дані, і є єдиною частиною ланцюжка, для якої символи, що складають її, не вказуються буквально в рядку даних (робити це, очевидно, було б неможливо, оскільки це повинно бути довше себе); Таким чином, це написано таким чином, що особливо легко відроджувати з себе. Форматор рядка даних складається з 241 майже однакових частин, кожна з яких формує конкретну дату з 241 в рядку даних.

Кожна частина програми може бути створена за допомогою рядка даних та її форматера наступним чином:

  • Щоб створити вихід, відформатуйте рядок даних зі зміщенням +8
  • Щоб створити формат даних рядка даних, відформатуйте рядок даних зі зміщенням +4, 241 раз
  • Щоб створити рядок даних, відформатуйте рядок даних, кодуючи її у вихідний формат
  • Щоб створити вступ, відформатуйте рядок даних із зміщенням -4

Тому все, що нам потрібно зробити, - це подивитися, як працюють ці частини програми.

Рядок даних

(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45» …

Нам потрібно просте кодування для рядка даних, оскільки ми повинні мати змогу змінити кодування в коді Mini-Flak. Ви не можете стати набагато простішим за це!

Ключова ідея цієї квоти (крім трюку коментарів) полягає в тому, що в основному є лише одне місце, де ми можемо зберігати великі обсяги даних: "суми повернених значень команд" в межах різних рівнів гніздування джерела програми. (Це зазвичай називається третім стеком, хоча у Mini-Flak немає другого стека, тому "робочий стек", ймовірно, краще ім'я в контексті Mini-Flak.) Іншими можливостями для зберігання даних буде головний / перший стек (який не працює тому що саме там повинен проходити наш вихід, і ми не можемо віддалено виводити повз сховище віддалено ефективним способом) і кодується в бінду в одному елементі стека (що не підходить для цієї проблеми, оскільки це потребує експоненціального часу для витягнути з нього дані); коли ви їх усунете, робочий стек - єдине місце, що залишилося.

Для того, щоб "зберігати" дані в цьому стеку, ми використовуємо незбалансовані команди (в цьому випадку перша половина (…)команди), які згодом будуть врівноважені у форматуванні рядків даних. Кожен раз, коли ми закриваємо одну з цих команд у форматі, вона підштовхує суму даної даних, взятої з рядка даних, і повертає значення всіх команд на цьому рівні вкладення у форматері; ми можемо гарантувати, що останні додають до нуля, тому форматник просто бачить окремі значення, взяті з рядка даних.

Формат дуже простий: з (наступними n копіями (), де n - номер, який ми хочемо зберегти. (Зверніть увагу, що це означає, що ми можемо зберігати лише негативні числа, і останній елемент рядка даних повинен бути позитивним.)

Одне злегка неінтуїтивних моментів щодо рядка даних полягає в тому, в якому порядку він знаходиться. "Старт" рядка даних - це кінець, що знаходиться ближче до початку програми, тобто самий зовнішній рівень вкладення; ця частина набуває останнього форматування (оскільки формат працює з найглибшого до зовнішнього рівня гніздування). Тим НЕ менше, незважаючи на те , відформатована в минулому, він отримує надрукований перший, оскільки значення в стек першим роздруковуються останнім інтерпретатором Mini-Flak. Цей же принцип застосовується і до програми в цілому; нам потрібно спершу відформатувати outro, потім формат даних рядка даних, потім рядок даних, потім вступ, тобто зворотній порядок, в якому вони зберігаються в програмі.

Форматор рядка даних

)[({})(
    ([({})]({}{}))
    {
        ((()[()]))
    }{}
    {
        {
            ({}(((({}())[()])))[{}()])
        }{}
        (({}))
        ((()[()]))
    }{}
)]{}

Форматор рядка даних складається з 241 розділів, кожен з яких має однаковий код (один розділ має незначно різний коментар), кожен з яких формує один конкретний символ рядка даних. (Тут ми не могли використати цикл: нам потрібен неврівноважений, )щоб прочитати рядок даних шляхом відповідності його незбалансованому (, і ми не можемо поставити один із них всередині {…}циклу, єдина форма циклу, яка існує. Отже, ми " розгорніть "форматер і просто отримайте вступ / вивід для виведення рядка даних із зміщенням форматера 241 разів.)

)[({})( … )]{}

Найбільш зовнішня частина елемента форматера читає один елемент рядка даних; простота кодування рядка даних призводить до невеликої складності в його читанні. Ми починаємо із закриття незрівняного (…)в рядку даних, потім заперечуємо ( […]) два значення: дату, яку ми щойно прочитали з рядка даних ( ({})), і повернене значення решти програми. Ми копіюємо повернене значення решти елемента форматера з (…)та додаємо копію до заперечної версії {}. Кінцевим результатом є те, що значення, що повертається елементом рядка даних та елементом форматера разом, є даним мінус мінус значення мінус значення повернення плюс значення повернення або 0; це необхідно для того, щоб наступний елемент рядка даних видав правильне значення.

([({})]({}{}))

Форматор використовує елемент верхнього стека, щоб знати, в якому режимі він перебуває (0 = формат у форматуванні рядків даних, будь-яке інше значення = зміщення для виведення). Однак, лише прочитавши рядок даних, дата знаходиться на вершині формату на стеку, і ми хочемо їх навпаки. Цей код є більш коротким варіантом коду підкачки Brain-Flak, який має вище b до b над a  +  b ; він не тільки коротший, але й корисніший (у цьому конкретному випадку), оскільки побічний ефект додавання b до a не є проблематичним, коли b дорівнює 0, а коли b - не 0, це робить для нас розрахунок зміщення.

{
    ((()[()]))
}{}
{
    …
    ((()[()]))
}{}

Brain-Flak має лише одну структуру потоку управління, тому, якщо ми хочемо нічого, крім whileциклу, це займе трохи роботи. Це структура "заперечення"; якщо на вершині стека є 0, він видаляє його, інакше він ставить 0 вгорі стека. (Це працює досить просто: доки на вершині стеку немає 0, натисніть 1 - 1 на стек двічі; коли ви закінчите, викладіть верхній елемент стека.)

Можна розмістити код всередині заперечної структури, як показано тут. Код запускається лише в тому випадку, якщо верхня частина стека була ненульовою; тому якщо у нас є дві заперечні структури, якщо припустити, що два верхніх елемента стека не є обома нульовими, вони скасують один одного, але будь-який код всередині першої структури запуститься лише в тому випадку, якщо верхній елемент стека був ненульовим, а код всередині друга структура працюватиме лише в тому випадку, якщо верхній елемент стека дорівнював нулю. Іншими словами, це еквівалент твердження if-then-else.

У пункті "тоді", яке запускається, якщо формат ненульовий, ми насправді не маємо нічого робити; що ми хочемо - це підсунути дані + зміщення до основного стеку (щоб він міг виводитися в кінці програми), але це вже є. Тож нам залишається мати справу лише з випадком кодування елемента рядка даних у вихідному вигляді.

{
    ({}(((({}())[()])))[{}()])
}{}
(({}))

Ось як ми це робимо. {({}( … )[{}()])}{}Структура повинна бути знайома як цикл з певним числом ітерацій (який працює шляхом переміщення лічильника циклу до робочої стеку і утримуючи її там, вона буде в безпеці від будь-якого іншого коду, так як доступ до робочої стеці прив'язаний до рівень гніздування програми). Тіло циклу є ((({}())[()])), що робить три копії верхнього елемента стека і додає 1 до нижнього. Іншими словами, він перетворює 40 на вершині стека в 40 вище 40 вище 41, або розглядається як ASCII, (в ((); працює цей раз буде робити (в (()в (()()в (()()()і так далі, і , таким чином , являє собою простий спосіб для створення нашої рядки даних (за умови , що є (на вершині стека вже).

Як тільки ми закінчимо цикл, (({}))дублює верхню частину стека (так що тепер вона починається, ((()…а не (()…. Ведуча (буде використана наступною копією форматера рядка даних для форматування наступного символу (він розширить його на (()(()…потім (()()(()…, і так далі, тому це генерує розділення (в рядку даних).

%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'

Існує ще один останній інтерес у форматуванні рядків даних. Гаразд, тому в основному це просто перенесений 4 кодові точки вниз; однак цей апостроф наприкінці може виглядати поза місцем. '(кодова точка 39) перетвориться на +(кодова точка 43), що не є командою Brain-Flak, тому ви, можливо, здогадалися, що вона існує для якоїсь іншої мети.

Причина тут полягає в тому, що формат даних рядків очікує, що там вже буде (стек (він ніде не містить буквальних 40). The'насправді на початку блоку, який повторюється, щоб скласти формат даних рядків даних, а не в кінці, тому після того, як символи форматора рядка даних будуть висунуті на стек (і код ось-ось перейде до друку рядка даних Сам по собі outro перетворює 39 на вершині стека в 40, готовий для того, щоб форматер (сам запущений формат цього разу, а не його представлення в джерелі) використовувати його. Ось чому у нас є "майже 241" копії форматера; у першій копії відсутній її перший символ. І цей персонаж, апостроф, є одним із лише трьох символів у рядку даних, які не відповідають коду Mini-Flak десь у програмі; це там суто як метод забезпечення константи.

Вступ та аутро

(((()()()()){}))
{{}
    (({})[(()()()())])
    (({})(
        {{}{}((()[()]))}{}
        (((((((({})){}){}{})){}{}){}){}())
        {
            ({}(
                (␀␀!S␠su! … many more comment characters … oq␝qoqoq)
                …
            )[{}()])
        }{}
        {}({}())
    )[{}])
    (({})(()()()()){})
}{}{}␊

Вступ і вивід є концептуальною частиною програми; Єдина причина, яку ми вирізняємо, полягає в тому, що outro потрібно виводити перед тим, як рядок даних та його форматер (щоб він друкував після них), тоді як вступ потрібно виводити після них (друкуючи перед ними).

(((()()()()){}))

Почнемо, помістивши дві копії з 8 на стек. Це зсув для першої ітерації. Друга копія полягає в тому, що основний цикл очікує, що над версією стека буде елемент небажаної зсуву, залишений від тесту, який вирішує, чи існує головна петля, і тому нам потрібно помістити туди непотрібний елемент, щоб це не викидає елемент, якого ми насправді хочемо; копія - це найшвидший (таким чином, найшвидший спосіб отримання) спосіб зробити це.

Є й інші подання числа 8, які не довше цього. Однак, коли йдеться про найшвидший код, це, безумовно, найкращий варіант. По-перше, використання ()()()()швидше, ніж, скажімо, (()()){}тому що, незважаючи на те, що обидва є 8 символами, перший - цикл швидше, тому що (…)рахується як 2 цикли, але ()лише як один. Збереження одного циклу є мізерним порівняно з набагато більшим врахуванням для : (і )мати набагато менші кодові точки, ніж {та }, тому генерування для них фрагменту даних буде набагато швидше (і фрагмент даних займе менше місця в коді, теж).

{{} … }{}{}

Основна петля. Це не враховує ітерацій (це whileцикл, а не forцикл, і для тестування використовується тест). Як тільки він виходить, ми відкидаємо два верхні елементи стека; верхній елемент - нешкідливий 0, але внизу елемент буде "форматом, який слід використовувати при наступній ітерації", який (будучи негативним зміщенням) - це від'ємне число, і якщо на стеку є якісь негативні цифри, коли Mini -Flak програма виходить, інтерпретатор виходить з ладу, намагаючись вивести їх.

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

(({})[(()()()())])

Цей код натискає 4 і f  - 4 над елементом стеки f , залишаючи цей елемент на місці. Ми обчислюємо формат для наступної ітерації заздалегідь (хоча у нас є постійні 4 зручні) і одночасно отримуємо стек у правильному порядку для наступних кількох частин програми: ми будемо використовувати f як формат для ця ітерація, і 4 потрібні перед цим.

(({})( … )[{}])

Це зберігає копію f  - 4 на робочому стеку, щоб ми могли використовувати її для наступної ітерації. (Значення f все ще буде присутнє в цій точці, але воно буде в незручному місці на стеку, і навіть якщо ми зможемо маневрувати до потрібного місця, нам доведеться провести цикли, віднімаючи від нього 4, і цикли друку коду, щоб зробити це віднімання. Набагато простіше просто зараз зберігати його.)

{{}{}((()[()]))}{}

Тест на перевірку того, чи є зміщення 4 (тобто f  - 4 - 0). Якщо це так, ми друкуємо формат даних рядків даних, тому нам потрібно запустити рядок даних та його форматтер 241 раз, а не один раз при цьому зміщення. Код досить простий: якщо f  - 4 ненульовий, замініть f  - 4 і сам 4 парою нулів; то в будь-якому випадку вставте верхній елемент стека. Тепер у нас на стеці є число вище f , або 4 (якщо ми хочемо надрукувати цю ітерацію 241 раз), або 0 (якщо ми хочемо надрукувати його лише один раз).

(
    ((((((({})){}){}{})){}{}){}){}
    ()
)

Це цікавий вид константи Brain-Flak / Mini-Flak; довгий рядок тут позначає число 60. Вас можуть збентежити відсутність (), які зазвичай є константами Брейн-Флак; це не звичайне число, а церковна цифра, яка інтерпретує числа як операцію дублювання. Наприклад, церковна цифра 60, яку можна побачити тут, робить 60 примірників своїх даних і об'єднує їх усі разом в одне значення; у Brain-Flak єдине, що ми можемо поєднати - це звичайні цифри, до того ж ми додаємо 60 копій верхньої частини стека і таким чином множимо верхню частину стека на 60.

Як бічну примітку, ви можете використовувати шукач чисел Underload , який генерує церковні цифри в синтаксисі Underload, щоб знайти відповідне число і в Mini-Flak. Число недовантаження (крім нуля) використовує операції "дублювати верхній елемент стека" :та "поєднувати два верхні елементи стека" *; обидві ці операції існують у Brain-Flak, тому ви просто перекладете :на ), *до {}, додайте а {}та додайте достатньо (на початку для балансування (для цього використовується дивна суміш основного стека та робочого стека, але це працює).

Цей конкретний фрагмент коду використовує церковну цифру 60 (фактично фрагмент "помножити на 60") разом із збільшенням, щоб генерувати вираз 60 x  + 1. Отже, якщо у нас було 4 з попереднього кроку, це дає нам значення 241, або якщо у нас було 0, ми просто отримуємо значення 1, тобто це правильно обчислює кількість необхідних нам ітерацій.

Вибір 241 не випадковий; це значення було обрано таким чином: а) приблизно довжина, при якій програма все-таки закінчиться, і b) 1 більше, ніж 4 рази кругле число. Круглі цифри, 60 у цьому випадку, як правило, мають коротші зображення як церковні цифри, оскільки у вас є більш гнучкість факторів для копіювання. Програма згодом містить прокладки, щоб точно довести довжину до 241.

{
    ({}(
        …
    )[{}()])
}{}

Це цикл for, як той, який бачили раніше, який просто запускає код всередині нього кілька разів, рівний верхній частині основного стека (який він споживає; сам лічильник циклу зберігається на робочому стеку, але видимість що прив'язане до рівня гніздування програми, і тому неможливо нічого, крім циклу, взаємодіяти з нею). Це фактично запускає рядок даних та його форматтер 1 або 241 раз, і оскільки ми вискакували всі значення, які ми використовували для нашого обчислення потоку управління з основного стеку, ми маємо формат, який слід використовувати поверх нього, готовий до форматер, який потрібно використовувати.

(␀␀!S␠su! … many more comment characters … oq␝qoqoq)

Коментар тут не зовсім без інтересу. Для одного - є кілька команд Brain-Flak; )в кінці природно генеруються в якості побічного ефекту шляху переходи між різними сегментами роботи програми, так що (на початку були вручну додані , щоб збалансувати його (і не дивлячись на довжину коментар зсередини, поставивши коментар всередині ()команда по - , як і раніше ()команда, так що все це робить додавання 1 до поверненню значенню рядка даних і її форматіровщіку що - то , що цикл повністю ігнорує).

Більше того, що ці символи NUL на початку коментаря явно не компенсуються ні з чого (навіть різниці між +8 і -4 недостатньо, щоб перетворити (нуль на NUL). Це чиста підкладка, щоб довести рядок даних 239 елементів до 241 елемента (що легко платить за себе: знадобиться набагато більше двох байтів, щоб генерувати 1 проти 239, а не 1 проти 241 при розрахунку кількості необхідних ітерацій ). NUL був використаний як символ прокладки, оскільки він має найменшу можливу кодову точку (робить вихідний код для рядка даних коротшим і тим самим швидшим для виведення).

{}({}())

Відкиньте верхній елемент стека (формат, який ми використовуємо), додайте 1 до наступного (останній символ, який потрібно вивести, тобто перший символ, який слід надрукувати, в розділі програми, який ми щойно відформатували). Старий формат нам більше не потрібен (новий формат ховається на робочому стеку); і приріст у більшості випадків є нешкідливим і змінює 'на одному кінці вихідного подання форматник рядків даних на формат ((який необхідний на стеці для наступного запуску форматера, щоб відформатувати сам рядок даних). Нам потрібна така трансформація в outro або intro, тому що змушення кожного елемента форматування рядка даних починати з (нього зробить його дещо складнішим (оскільки нам потрібно буде закрити, (а потім скасувати його ефект згодом), інам би хотілося (десь генерувати додаткові, оскільки у нас є майже 241 копії форматера, а не всі 241 (тож найкраще, щоб такий нешкідливий персонаж 'був таким, якого не вистачає).

(({})(()()()()){})

Нарешті, тест виходу з циклу. Поточна вершина основного стека - це потрібний нам формат для наступної ітерації (щойно повернувся з робочого стеку). Це копіює його і додає 8 до копії; отримане значення буде відкинуто наступного разу навколо циклу. Однак якщо ми щойно надрукували вступ, зміщення склало -4, так що зміщення для "наступної ітерації" складе -8; -8 + 8 дорівнює 0, тому цикл вийде, а не продовжить ітерацію після цього.


16

128,673,515 циклів

Спробуйте в Інтернеті

Пояснення

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


Цей блок коду має дві вимоги.

  • Він повинен приймати число і виводити лише код символу для цього символу

  • Потрібно легко відтворювати таблицю пошуку по шматочках у Brain-Flak

Для побудови цього блоку я фактично використав метод із мого доказу, що Miniflak є Тьюрінгом завершеним. Для кожної даної є блок коду, який виглядає приблизно так:

(({}[()])[(())]()){(([({}{})]{}))}{}{(([({}{}(%s))]{}))}{}

Це віднімає одне число від числа вгорі стека і якщо нуль проштовхує %sдату під ним. Оскільки кожен фрагмент зменшує розмір на одиницю, якщо ви починаєте з n у стеці, ви отримаєте назад n-ту дату.

Це приємно і модульно, тому програма може бути легко написана.


Далі ми повинні встановити машину, яка фактично переводить цю пам'ять у джерело. Він складається з 3 частин як таких:

(([()]())())
{({}[(
  -Look up table-
 )]{})
 1. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}(([{}]))(()()()()()))]{})}{}

 2. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}
      (({}[(
      ({}[()(((((()()()()()){}){}){}))]{}){({}[()(({}()))]{}){({}[()(({}((((()()()){}){}){}()){}))]{}){({}[()(({}()()))]{}){({}[()(({}(((()()()()())){}{}){}))]{}){([(({}{}()))]{})}}}}}{}
      (({}({}))[({}[{}])])
     )]{}({})[()]))
      ({[()]([({}({}[({})]))]{})}{}()()()()()[(({}({})))]{})
    )]{})}{}

 3. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}
     (({}(({}({}))[({}[{}])][(
     ({}[()(
      ([()](((()()[(((((((()()()){})())){}{}){}){})]((((()()()()())){}{}){})([{}]([()()](({})(([{}](()()([()()](((((({}){}){}())){}){}{}))))))))))))
     )]{})
     {({}[()(((({})())[()]))]{})}{}
     (([(((((()()()()){}){}()))){}{}([({})]((({})){}{}))]()()([()()]({}(({})([()]([({}())](({})([({}[()])]()(({})(([()](([({}()())]()({}([()](([((((((()()()())()){}){}){}()){})]({}()(([(((((({})){}){}())){}{})]({}([((((({}())){}){}){}()){}()](([()()])(()()({}(((((({}())())){}{}){}){}([((((({}))){}()){}){}]([((({}[()])){}{}){}]([()()](((((({}())){}{}){}){})(([{}](()()([()()](()()(((((()()()()()){}){}){}()){}()(([((((((()()()())){}){}())){}{})]({}([((((({})()){}){}){}()){}()](([()()])(()()({}(((((({}){}){}())){}){}{}(({})))))))))))))))))))))))))))))))))))))))))))))))
     )]{})[()]))({()()()([({})]{})}{}())
    )]{})}{}

   ({}[()])
}{}{}{}
(([(((((()()()()){}){}())){}{})]((({}))([()]([({}())]({}()([()]((()([()]((()([({})((((()()()()){}){}()){})]()())([({})]({}([()()]({}({}((((()()()()()){}){}){}))))))))))))))))))

Машина складається з чотирьох частин, які працюють в порядку, починаючи з 1 і закінчуючи 3. Я позначив їх у коді вище. Кожен розділ також використовує той самий формат таблиці пошуку, який я використовую для кодування. Це тому, що вся програма міститься в циклі, і ми не хочемо запускати кожен розділ кожного разу, коли ми проходимо через цикл, тому ми вкладаємо ту саму структуру RA і запитуємо той розділ, який ми бажаємо кожен раз.

1

Розділ 1 - це простий розділ налаштування.

Програма повідомляє перші запити, розділ 1 та дата 0. Дата 0 не існує, тому замість повернення цього значення вона просто зменшує запит один раз для кожної дати. Це корисно, оскільки ми можемо використати результат для визначення кількості даних, що стане важливим у наступних розділах. У розділі 1 записується кількість даних шляхом негативізації результату та запитів Розділ 2 та остання дата. Єдина проблема полягає в тому, що ми не можемо запитувати розділ 2 безпосередньо. Оскільки залишився ще один декремент, нам потрібно запитати неіснуючий розділ 5. Насправді це буде так щоразу, коли ми запитуємо розділ в іншому розділі. Я проігнорую це в своєму поясненні, проте якщо ви шукаєте код, просто запам'ятайте, 5 означає повернутися до розділу, а 4 означає запустити той же розділ ще раз.

2

Розділ 2 декодує дані символами, що складають код після блоку даних. Кожен раз, коли він очікує, що стек з'явиться так:

Previous query
Result of query
Number of data
Junk we shouldn't touch...

Він відображає кожен можливий результат (число від 1 до 6) на один із шести дійсних символів Miniflak ( (){}[]) і розміщує його нижче кількості даних за допомогою "Небажаної ми не повинні торкатися". Це отримує нам стек на кшталт:

Previous query
Number of data
Junk we shouldn't touch...

Звідси нам потрібно або запитувати наступну дату, або, якщо ми їх запитували, всі переходимо до розділу 3. Попередній запит насправді не точний надсилається запит, а швидше запит мінус кількість даних у блоці. Це пояснюється тим, що кожне дане зменшує запит по одному, тому запит виходить досить непростий. Для створення наступного запиту додаємо копію кількості даних і віднімаємо. Тепер наш стек виглядає так:

Next query
Number of data
Junk we shouldn't touch...

Якщо наш наступний запит дорівнює нулю, ми прочитали всю пам'ять, необхідну в розділі 3, тому ми додаємо кількість даних до запиту ще раз і ляпаємо 4 вгорі стека, щоб перейти до розділу 3. Якщо наступний запит не дорівнює нулю, покладіть 5 на стек, щоб знову запустити секцію 2.

3

Розділ 3 робить блок даних, запитуючи нашу оперативну пам’ять так само, як це робить розділ 3.

Для стислості я опущу більшість деталей того, як працює розділ 3. Він майже ідентичний розділу 2, за винятком того, що замість того, щоб переводити кожну дату в один символ, вона переводить кожен у довгий фрагмент коду, що представляє його запис у оперативній пам'яті. Коли розділ 3 виконаний, він повідомляє програмі вийти з циклу.


Після запуску циклу програмі потрібно просто натиснути перший шматочок ланцюжка ([()]())(()()()()){({}[(. Я роблю це за допомогою наступного коду, що реалізує стандартні методи Колмогорова-складності.

(([(((((()()()()){}){}())){}{})]((({}))([()]([({}())]({}()([()]((()([()]((()([({})((((()()()()){}){}()){})]()())([({})]({}([()()]({}({}((((()()()()()){}){}){}))))))))))))))))))

Сподіваюся, це було зрозуміло. Будь ласка, коментуйте, якщо вас ніщо не бентежить.


Про те, скільки часу потрібно пробігти? Час на TIO.
Павло

@Pavel Я не запускаю його на TIO, тому що це буде надзвичайно повільно, я використовую той же інтерпретатор, який використовує TIO ( рубіновий ). На старий сервер стійок, до якого я маю доступ, потрібно близько 20 хвилин. Крейн-Флак займає близько 15 хвилин, але у Crain-Flak не встановлені прапори налагодження, тому я не можу забити його, не запустивши його в інтерпретаторі Ruby.
Пшеничний майстер

@Pavel я запустив його ще раз і приурочив. Потрібно 30m45.284sзавершити на досить низькому сервері (приблизно еквівалент середньому середньому робочому столу) за допомогою інтерпретатора ruby.
Пшеничний майстер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.