Fission , 1328 989 887 797 байт
Ця відповідь трохи необґрунтовано довгий (я б хотів, щоб у нас були регістри, що складаються ) ... будь ласка, не забудьте прокрутити повз це і показати іншим відповідям якусь любов!
Робота над цим кодом була тим, що надихнула цей виклик. Я хотів додати відповідь у Fission до EOEIS, що привело мене до цієї послідовності. Однак насправді вивчення Fission та його реалізація потребували декількох тижнів, працюючи над ним і вимикаючи його. Тим часом послідовність дійсно зросла на мені, тому я вирішив поставити окремий виклик для цього (плюс, це не було б особливо далеко вниз по дереву на EOEIS).
Тож представляю тобі, Чудовисько:
R'0@+\
/ Y@</ /[@ Y]_L
[? % \ / \ J
\$@ [Z/;[{+++++++++L
UR+++++++++>/;
9\ ; 7A9
SQS {+L /$ \/\/\/\/\/ 5/ @ [~ &@[S\/ \ D /8/
~4X /A@[ %5 /; & K } [S//~KSA /
3 \ A$@S S\/ \/\/\/ \/>\ /S]@A / \ { +X
W7 X X /> \ +\ A\ / \ /6~@/ \/
/ ~A\; +;\ /@
ZX [K / {/ / @ @ } \ X @
\AS </ \V / }SZS S/
X ;;@\ /;X /> \ ; X X
; \@+ >/ }$S SZS\+; //\V
/ \\ /\; X X @ @ \~K{
\0X / /~/V\V / 0W//
\ Z [K \ //\
W /MJ $$\\ /\7\A /;7/\/ /
4}K~@\ &] @\ 3/\
/ \{ }$A/1 2 }Y~K <\
[{/\ ;@\@ / \@<+@^ 1;}++@S68
@\ <\ 2 ; \ /
$ ;}++ +++++++L
%@A{/
M \@+>/
~ @
SNR'0YK
\ A!/
Очікується, що на вході не буде зафіксованого нового рядка, тому ви, можливо, захочете його назвати так echo -n 120 | ./Fission oeis256504.fis
.
Макет, можливо, все ще може бути більш ефективним, тому я думаю, що тут ще багато можливостей для вдосконалення (наприклад, він містить 911 581 461 374 пробілів).
Перш ніж ми перейдемо до пояснення, примітка про тестування цього: офіційний перекладач працює не так, як є. а) Mirror.cpp
не компілюється у багатьох системах. Якщо ви зіткнулися з цією проблемою, просто прокоментуйте рядовий рядок - зачіпаний компонент (випадкове дзеркало) не використовується в цьому коді. б) Є кілька помилок, які можуть призвести до невизначеної поведінки (і, ймовірно, буде для програми цей комплекс). Ви можете застосувати цей виправлення, щоб виправити їх. Щойно ви зробите це, ви зможете скласти перекладача
g++ -g --std=c++11 *.cpp -o Fission
Факт забави: ця програма використовує майже кожен компонент, який Fission може запропонувати, за винятком #
(випадкове дзеркало), :
(напівзеркальне відображення) -
або |
(звичайне дзеркало) та "
(режим друку).
Що на Землі?
Попередження: Це буде досить довго ... Я припускаю, що ви щиро зацікавлені в тому, як працює Fission і як можна програмувати його. Тому що, якщо ви цього не зробите, я не впевнений, як я міг би це підсумувати. (Наступний параграф дає загальний опис мови.)
Fission - це двовимірна мова програмування, де і дані, і потік управління представлені атомами, що рухаються через сітку. Якщо ви бачили або використовували Marbelous раніше, концепція повинна бути нечітко знайомою. Кожен атом має дві цілі властивості: негативну масу та довільну енергію. Якщо маса коли-небудь стає негативною, атом видаляється з сітки. У більшості випадків ви можете трактувати масу як "величину" атома, а енергію - як якусь мета-властивість, яку використовують декілька компонентів для визначення потоку атомів (тобто більшість видів перемикачів залежать від знаку енергія). Я буду позначати атоми через (m,E)
, коли це необхідно. На початку програми сітка починається з купки(1,0)
атоми, куди б ви не розмістили чотири компоненти UDLR
(де буква позначає напрямок, яким атом рухається спочатку). Потім дошка заповнюється цілою купою компонентів, які змінюють масу та енергію атомів, змінюють їх напрямки або роблять інші більш складні речі. Повний список дивіться на сторінці esolangs , але я познайомлю більшість із них у цьому поясненні. Ще одним важливим моментом (який програма використовує кілька разів) є те, що сітка є тороїдальною: атом, який потрапляє на будь-яку зі сторін, знову з’являється в протилежну сторону, рухаючись в тому ж напрямку.
Я написав програму в декілька менших частин і зібрав їх наприкінці, тож я буду проходити пояснення.
atoi
Цей компонент може здатися досить нецікавим, але він приємний і простий і дозволяє мені представити багато важливих концепцій арифметики та контрольного потоку Фіссіона. Тому я пройду цю деталь досить ретельно, тому я можу звести інші частини до впровадження нової механіки Fission та зазначення компонентів вищого рівня, за детальним контрольним потоком ви повинні мати змогу стежити за собою.
Fission може читати лише значення байтів з окремих символів, а не цілих чисел. Хоча це є прийнятною практикою тут, я зрозумів, що перебуваючи на цьому, я міг би зробити це правильно і проаналізувати фактичні цілі числа на STDIN. Ось atoi
код:
;
R'0@+\
/ Y@</ /[@ Y]_L
[? % \ / \ J
\$@ [Z/;[{+++++++++L
UR+++++++++>/;
O
Два найважливіші компоненти Fission - це реактори поділу та плавлення. Розділюючі реактори є будь-яким із V^<>
(вищевказаний код використовує <
та >
). Реактор ділення може зберігати атом (направляючи його в клин персонажа), за замовчуванням (2,0)
. Якщо атом потрапить на вершину персонажа, два нові атоми будуть відправлені в сторони. Їх маса визначається діленням вхідної маси на збережену масу (тобто вдвічі зменшується вдвічі) - лівий атом отримує це значення, а правий атом отримує решту маси (тобто маса зберігається при поділі) . Обидва вихідні атоми матимуть мінус вхідної енергіїнакопичена енергія. Це означає, що ми можемо використовувати реактори ділення для арифметики - як для віднімання, так і для ділення. Якщо в дію потрапить реактор поділу, атом просто відбивається по діагоналі і потім рухатиметься у напрямку до вершини персонажа.
Плавкі реактори є будь-яким із YA{}
(вищевказаний код використовує Y
та {
). Їх функція схожа: вони можуть зберігати атом (за замовчуванням (1,0)
), і при попаданні з вершини два нові атоми будуть відправлені в сторони. Однак у цьому випадку два атоми будуть однаковими, завжди зберігаючи надходить енергію та множуючи вхідну масу на збережену масу. Тобто, за замовчуванням термоядерний реактор просто дублює будь-який атом, що потрапив на його верхівку. При попаданні з боків термоядерні реактори дещо складніші: атом такожзберігається (незалежно від іншої пам'яті), поки атом не потрапить у протилежну сторону. Коли це станеться, новий атом вивільняється в напрямку до вершини, маса і енергія якої є сумою двох старих атомів. Якщо новий атом потрапить на ту саму сторону, перш ніж відповідний атом досягне протилежної сторони, старий атом просто буде перезаписаний. Плавкі реактори можна використовувати для здійснення додавання та множення.
Ще один простий компонент, який я хочу вийти з шляху, [
і ]
який просто задає напрямок атома вправо і вліво відповідно (незалежно від напрямку, що входить). Вертикальні еквіваленти M
(вниз) і W
(вгору), але вони не використовуються для atoi
коду. UDLR
також діють як WM][
після вивільнення своїх початкових атомів.
У будь-якому разі давайте подивимось на код там. Програма починається з 5 атомів:
R
І L
на дні просто отримати їх приріст маси (з +
) , щоб стати , (10,0)
а потім зберігали в розподілі і термоядерного реактора, відповідно. Ми будемо використовувати ці реактори для розбору входу базової 10.
- У
L
верхньому правому куті його маса зменшується (з _
), щоб вона стала, (0,0)
і зберігається в стороні термоядерного реактора Y
. Це потрібно відслідковувати число, яке ми читаємо - ми поступово збільшуватимемо та множимо це, коли читатимемо числа.
- У
R
верхньому лівому куті його маса встановлюється символьним кодом 0
(48) з '0
, потім маса і енергія змінюються @
і, нарешті, збільшуються один раз, +
щоб дати (1,48)
. Потім його перенаправляють за допомогою діагональних дзеркал \
і /
зберігають у реакторі поділу. Ми будемо використовувати 48
для віднімання, щоб перетворити вхід ASCII у фактичні значення цифр. Ми також повинні були збільшити масу, 1
щоб уникнути поділу на 0
.
- Нарешті,
U
лівий нижній кут - це те, що насправді приводить все в рух і спочатку використовується лише для управління потоком.
Після перенаправлення вправо, керуючий атом потрапляє ?
. Це вхідний компонент. Він зчитує символ і встановлює масу атома на прочитане значення ASCII та енергію на 0
. Якщо ми натиснемо EOF замість цього, енергія буде встановлена на 1
.
Атом продовжується і потім б’є %
. Це дзеркальний вимикач. Для позитивної енергії це діє як /
дзеркало. Але для позитивної енергії вона діє як \
(а також зменшує енергію на 1). Тож поки ми читаємо символи, атом буде відображено вгору, і ми можемо обробити персонажа. Але коли ми закінчимо з введенням, атом буде відображено вниз, і ми зможемо застосувати різну логіку для отримання результату. FYI, протилежна складова &
.
Отже, зараз у нас атом рухається. Що ми хочемо зробити для кожного символу - це прочитати його значення цифри, додати його до загальної кількості, а потім помножити цей загальний обсяг на 10, щоб підготуватися до наступної цифри.
Перший характер атома потрапляє на термоядерний реактор (за замовчуванням) Y
. Це розбиває атом, і ми використовуємо ліву копію як атом керування, щоб повернутися до вхідного компонента і прочитати наступний символ. Права копія буде оброблена. Розглянемо випадок, коли ми прочитали персонажа 3
. Наш атом буде (51,0)
. Ми обмінюємось масою та енергією @
, щоб ми могли використати віднімання наступного реактора ділення. Реактор віднімає 48
енергію (не змінюючи масу), тому він відправляє дві копії (0,3)
- енергія тепер відповідає цифрі, яку ми прочитали. Вихідна копія просто відкидається ;
(компонент, який просто знищує всі вхідні атоми). Ми продовжуватимемо працювати зі спадною копією. Вам потрібно буде слідувати його шляху через/
і \
дзеркала трохи.
@
Безпосередньо перед термоядерним реактором SWAPS маси і енергії знову, таким чином, що ми додамо (3,0)
до нашого наростаючим підсумком в Y
. Зверніть увагу, що сам по собі біг буде завжди мати 0
енергію.
Зараз J
- стрибок. Що вона робить, це стрибати будь-який вхідний атом вперед за своєю енергією. Якщо це так 0
, атом просто продовжує рухатися прямо. Якщо це, 1
то вона пропустить одну клітинку, якщо вона 2
пропустить дві комірки тощо. Енергія витрачається в стрибку, тому атом завжди закінчується енергією 0
. Оскільки загальний обсяг має нульову енергію, стрибок на даний момент ігнорується, і атом перенаправляється в реактор синтезу, {
який помножує його масу на 10
. Знижуюча копія відкидається, ;
тоді як подаюча копія подається назад в Y
реактор як новий загальний обсяг.
Вищезазначене постійно повторюється (у кумедний конвеєрний спосіб, коли обробляються нові цифри до того, як будуть зроблені попередні), поки ми не натиснемо EOF. Тепер %
воля відправить атом вниз. Ідея полягає в тому, щоб перетворити цей атом (0,1)
тепер, перш ніж потрапити на працюючий загальний реактор, щоб а) на загальну кількість не вплинуло (нульова маса) і б) ми отримаємо енергію, 1
щоб перестрибнути через [
. Ми можемо легко піклуватися про енергію $
, яка збільшує енергію.
Проблема полягає в тому, ?
що не скидається маса при натисканні на EOF, тому маса все одно буде останньою, прочитаної символом, і енергія буде 0
(бо %
зменшилася 1
спина до 0
). Тож ми хочемо позбутися цієї маси. Для цього ми знову обмінюємось масою та енергією @
.
Мені потрібно ввести ще один компонент , перш ніж закінчити цей розділ: Z
. Це по суті те саме, що %
або &
. Різниця полягає в тому, що вона дозволяє атомам позитивної енергії проходити прямо (при зменшенні енергії) і відхиляє атоми непозитивної енергії на 90 градусів зліва. Ми можемо використати це для усунення енергії атома, перебираючи його через Z
і знову - як тільки енергія піде, атом відхилиться і залишить цикл. Ось така закономірність:
/ \
[Z/
де атом рухатиметься вгору, коли енергія дорівнює нулю. Я буду використовувати цю схему в тій чи іншій формі кілька разів в інших частинах програми.
Таким чином , коли атом покидає цю маленьку петлю, він буде (1,0)
і місць , щоб (0,1)
самі @
до потрапляння в реакторі злитого випустити кінцевий результат введення. Однак загальний обсяг буде відключений в 10 разів, оскільки ми вже попередньо помножили його на іншу цифру.
Тож тепер з енергією 1
цей атом буде пропускати [
і стрибати в /
. Це відводить його в реактор ділення, який ми підготували розділити на 10 і зафіксувати наше стороннє множення. Знову ж таки, ми відкидаємо одну половину, ;
а іншу зберігаємо як вихід (тут представлено, O
що просто надрукує відповідний символ і знищить атом - у повній програмі ми замість цього продовжуємо використовувати атом).
itoa
/ \
input -> [{/\ ;@
@\ <\
$ ;}++ +++++++L
%@A{/
M \@+>/
~ @
SNR'0YK
\ A!/
Звичайно, нам також потрібно перетворити результат назад у рядок і роздрукувати його. Ось для чого ця частина. Це передбачає, що вхід не надходить до позначки 10 або близько того, але в повній програмі, яка легко дається. Цей біт можна знайти в нижній частині повної програми.
Цей код представляє новий дуже потужний компонент Fission: стек K
. Стек спочатку порожній. Коли атом з негативною енергією потрапляє в стек, атом просто висувається на стек. Коли атом з негативною енергією потрапить у стек, його масу та енергію замінять атом у верхній частині стека (який, таким чином, спливає). Якщо стек порожній, напрямок атома змінюється і його енергія стає позитивною (тобто множиться на -1
).
Добре, повернімося до фактичного коду. Ідея itoa
фрагмента полягає в тому, щоб багаторазово приймати модуль 10 введення, щоб знайти наступну цифру, при цьому ціле ділення введення на 10 для наступної ітерації. Це дасть усі цифри у зворотному порядку (від найменш значущих до найбільш значущих). Щоб виправити замовлення, ми натискаємо всі цифри на стек і наприкінці спливаємо їх по черзі, щоб надрукувати їх.
У верхній половині коду робиться обчислення цифр: L
з плюсами дає 10, яке ми клонуємо і подаємо в реактор поділу та синтезу, щоб ми могли розділити і помножити на 10. Петля по суті починається після [
верхнього лівого кута . Поточне значення ділиться: одна копія ділиться на 10, потім множиться на 10 і зберігається в реакторі поділу, який потім потрапляє в іншу копію на вершині. Це обчислює i % 10
як i - ((i/10) * 10)
. Зауважимо також, що A
розбиває проміжний результат після ділення і перед множенням, таким чином, щоб ми могли перейти i / 10
до наступної ітерації.
%
Перерве цикл коли змінні ітерації хітів 0. Так як це більш-менш робити щось час циклу, цей код буде працювати навіть для друку 0
(без створення провідних нулів в іншому випадку). Щойно ми залишаємо цикл, ми хочемо виповнити стек і надрукувати цифри. S
є протилежною Z
, тому це перемикач, який буде відхиляти вхідний атом з позитивною енергією на 90 градусів праворуч. Таким чином, атом насправді рухається через край від S
прямої до точки, K
щоб вискочити цифру (зверніть увагу на те, ~
що забезпечує вхідний атом енергії -1
). Ця цифра збільшується 48
на отримання коду ASCII відповідного символу. A
Розщеплює цифру надрукувати одну копію з!
і подати іншу копію назад в Y
реактор для наступної цифри. Друкована копія використовується як наступний тригер для стека (зауважте, що дзеркала також надсилають її по краю, щоб натиснути M
ліворуч).
Коли стек порожній, K
воля відобразить атом і перетворить його енергію в +1
такий, що він пройде прямо через S
. N
друкує новий рядок (тільки тому, що це акуратно :)). І тоді атом R'0
знову повертається, щоб опинитися в стороні Y
. Оскільки більше немає атомів навколо, це ніколи не буде випущено, і програма припиняється.
Обчислення номера ділення: Рамка
Давайте перейдемо до власне м’яса програми. Код, по суті, є портом моєї реалізації математичного довідника:
fission[n_] := If[
(div =
SelectFirst[
Reverse@Divisors[2 n],
(OddQ@# == IntegerQ[n/#]
&& n/# > (# - 1)/2) &
]
) == 1,
1,
1 + Total[fission /@ (Range@div + n/div - (div + 1)/2)]
]
де div
кількість цілих чисел у максимальному розділі.
Основні відмінності полягають у тому, що ми не можемо мати справу з значеннями напів цілого числа у Fission, тому я роблю багато речей, помножених на два, і що у Fission немає рекурсії. Щоб обійти це, я висуваю всі цілі числа в розділі в черзі, щоб пізніше їх обробити. Кожне число, яке ми обробляємо, збільшуватимемо лічильник на одне, і як тільки черга буде порожньою, ми випустимо лічильник та відправимо його на друк. (Черга, Q
працює точно так само, як K
раз у порядку FIFO.)
Ось рамка для цієї концепції:
+--- input goes in here
v
SQS ---> compute div from n D /8/
~4X | /~KSA /
3 +-----------> { +X
initial trigger ---> W 6~@/ \/
4
W ^ /
| 3
^ generate range |
| from n and div <-+----- S6
| -then-
+---- release new trigger
Найважливіші нові компоненти - це цифри. Це телепортери. Всі телепортери з однаковою цифрою належать разом. Коли атом потрапить на будь-який телепортер, він буде негайно переміщений наступним телепортером у тій же групі, де наступний визначається у звичайному порядку вліво-вправо, зверху вниз. Це не обов'язково, але допоможіть у компонуванні (а значить, і в гольфі трохи). Є також, X
що просто дублює атом, надсилаючи одну копію прямо вперед, а іншу назад.
На даний момент ви, можливо, зможете самі розібратися у більшості рамок. У верхньому лівому куті є черга значень, які ще обробляються, і випускає по черзі n
. Один екземпляр n
телепортується донизу, тому що він нам потрібен при обчисленні діапазону, інший примірник переходить до блоку вгорі, який обчислює div
(це далеко не найбільший розділ коду). Після того, div
як було обчислено, він дублюється - одна копія збільшує лічильник у верхньому правому куті, в якому зберігається K
. Інший примірник телепортується донизу. Якщо це div
було 1
, ми негайно відхиляємо його вгору і використовуємо як тригер для наступної ітерації, не завдаючи жодних нових значень. Інакше ми використовуємо div
іn
у розділі внизу, щоб генерувати новий діапазон (тобто потік атомів із відповідними масами, які згодом ставлять у чергу), а потім відпустити новий тригер після завершення діапазону.
Після того, як черга буде порожньою, тригер буде відображений, проходячи прямо через S
і знову з'явившись у верхньому правому куті, де він вивільняє лічильник (кінцевий результат) з A
якого потім телепортується itoa
через 8
.
Обчислення номера ділення: Тіло петлі
Отже, все, що залишилося - це два розділи для обчислення div
та генерації діапазону. Обчислення div
- це ця частина:
;
{+L /$ \/\/\/\/\/ 5/ @ [~ &@[S\/ \
/A@[ %5 /; & K } [S/
\ A$@S S\/ \/\/\/ \/>\ /S]@A / \
X X /> \ +\ A\ / \ /
/ ~A\; +;\ /@
ZX [K / {/ / @ @ } \ X @
\AS </ \V / }SZS S/
X ;;@\ /;X /> \ ; X X
\@+ >/ }$S SZS\+; //\V
/ \\ /\; X X @ @ \~K{
\0X / /~/V\V / 0W//
\ Z [K \ //\
\ /\7\A /;7/\/
Ви, напевно, вже зараз бачили достатньо, щоб розібратися в цьому для себе з деяким терпінням. Розбивка на високому рівні така: Перші 12 стовпців або близько того генерують потік дільників 2n
. Наступні 10 стовпців фільтрують ті, які не задовольняють OddQ@# == IntegerQ[n/#]
. Наступні 8 стовпців фільтрують ті, які не задовольняють n/# > (# - 1)/2)
. Нарешті, ми висуваємо всі дійсні дільники на стек, і як тільки ми закінчимо, ми випорожнюємо весь стек в термоядерний реактор (перезаписуючи всі, крім останнього / найбільшого дільника), а потім випускаємо результат з подальшим усуненням його енергії (яка не була -зеро від перевірки нерівності).
Там є безліч шалених доріжок, які насправді нічого не роблять. Переважно \/\/\/\/
божевілля вгорі ( 5
s також є його частиною) та одна стежка навколо низу (що проходить через 7
s). Мені довелося додати це, щоб боротися з деякими неприємними умовами гонки. Fission може використовувати компонент затримки ...
Код, що генерує новий діапазон, n
і div
це такий:
/MJ $$\
4}K~@\ &] @\ 3/\
\{ }$A/1 2 }Y~K <\
\@ / \@<+@^ 1;}++@
2 ; \ /
Спочатку обчислюємо n/div - (div + 1)/2
(обидва терміни, які дають однаковий результат) і зберігаємо на потім. Тоді ми генеруємо діапазон від div
вниз до 1
і додаємо збережене значення до кожного з них.
В обох цих двох нових загальних моделей, які я повинен зазначити: Один є SX
або ZX
потрапляє знизу (або обертові версії). Це хороший спосіб дублювання атома, якщо ви хочете, щоб одна копія йшла прямо вперед (оскільки перенаправлення виходів термоядерного реактора іноді може бути громіздким). В S
або Z
обертається атом на , X
а потім повертає дзеркальну копію назад у вихідне напрям поширення.
Інша закономірність є
[K
\A --> output
Якщо ми зберігаємо в ньому будь-яке значення, K
ми можемо неодноразово отримувати його, вражаючи K
негативною енергією зверху. A
Дублює значення ми зацікавлені в тому, і посилає то , що копіювати правий на стек на наступний раз , коли нам це потрібно.
Що ж, це був цілком фальшивий ... але якщо ви насправді пережили це, я сподіваюся, ви зрозуміли, що Fission i͝s̢̘̗̗ ͢i̟nç̮̩r̸̭̬̱͔e̟̹̟̜͟d̙i̠͙͎̖͓̯b̘̠͎̭̰̼l̶̪̙̮̥̮y̠̠͎̺͜ ͚̬̮f̟͞u̱̦̰͍n͍ ̜̠̙t̸̳̩̝o ̫͉̙͠p̯̱̭͙̜͙͞ŕ̮͓̜o̢̙̣̭g̩̼̣̝r̩̼̣̝a̪̜͇m̳̭͔̤̞ͅ ͕̺͉̫̀ͅi͜n̳̯̗̳͇̹.̫̞̲̞̜̳