GNU Prolog, 98 байт
b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
Ця відповідь є чудовим прикладом того, як Prolog може боротися навіть з найпростішими форматами вводу / виводу. Він працює в справжньому стилі Prolog, описуючи проблему, а не алгоритм її вирішення: він визначає, що вважається законним розташуванням бульбашок, просить Prolog генерувати всі ці компонування міхурів, а потім підраховує їх. Покоління займає 55 символів (перші два рядки програми). Підрахунок і введення / виведення приймають інші 43 (третій рядок і новий рядок, який розділяє дві частини). Гадаю, це не проблема, що ОП, як очікується, спричинить боротьбу мов із введенням / виводу! (Примітка. Підсвічування синтаксису Stack Exchange робить це важче для читання, а не простіше, тому я його вимкнув).
Пояснення
Почнемо з версії псевдокоду подібної програми, яка насправді не працює:
b(Bubbles,Count) if map(b,Bubbles,BubbleCounts)
and sum(BubbleCounts,InteriorCount)
and Count is InteriorCount + 1
and is_sorted(Bubbles).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
Це має бути досить зрозуміло, як це bпрацює: ми представляємо бульбашки за допомогою відсортованих списків (що є простою реалізацією мультисетів, що змушує рівних мультисетів порівняти рівними), а один міхур []має кількість 1, а більший міхур має кількість дорівнює загальній кількості бульбашок всередині плюс 1. Для підрахунку 4 ця програма (якщо вона працювала) генерує такі списки:
[[],[],[],[]]
[[],[],[[]]]
[[],[[],[]]]
[[],[[[]]]]
[[[]],[[]]]
[[[],[],[]]]
[[[],[[]]]]
[[[[],[]]]]
[[[[[]]]]]
Ця програма є непридатною як відповідь з кількох причин, але найактуальнішою є те, що Prolog насправді не має mapприсудка (а написання цього зайняло б занадто багато байт). Тому замість цього ми пишемо програму приблизно так:
b([], 0).
b([Head|Tail],Count) if b(Head,HeadCount)
and b(Tail,TailCount)
and Count is HeadCount + TailCount + 1
and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
Інша основна проблема тут полягає в тому, що він перейде в нескінченний цикл під час запуску через те, як працює порядок оцінки Prolog. Однак ми можемо вирішити нескінченний цикл, трохи переставивши програму:
b([], 0).
b([Head|Tail],Count) if Count #= HeadCount + TailCount + 1
and b(Head,HeadCount)
and b(Tail,TailCount)
and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
Це може виглядати досить дивно - ми додаємо підрахунки, перш ніж ми дізнаємося, що вони є, - але GNU Prolog #=здатний поводитись з такою різновидом арифметики, і тому що це перший рядок b, і той, HeadCountі інший TailCountповинен бути менше Count(що відомо), він служить методом природного обмеження, скільки разів може відповідати рекурсивний термін, і, таким чином, змушує програму завжди припинятися.
Наступний крок - трохи полегшити це. Видалення символів пробілів, використовуючи імена змінних з одного символу, використовуючи скорочення , як :-для ifі ,для and, використовуючи setofзамість listof(він має більш коротку назву і виробляє ті ж результати , в даному випадку), і використовуючи sort0(X,X)замість is_sorted(X)(бо is_sortedце на самому ділі не справжня функції, Я склав це):
b([],0).
b([H|T],N):-N#=A+B+1,b(H,A),b(T,B),sort0([H|T],[H|T]).
c(X,Y):-setof(A,b(A,X),L),length(L,Y).
Це досить коротко, але можна зробити і краще. Ключове розуміння полягає в тому, що [H|T]це насправді багатослівний під час переліку синтаксисів списку. Як знають програмісти Lisp, список в основному складається з комірок мінусів, які в основному є лише кортежами, і навряд чи будь-яка частина цієї програми використовує вбудовані списки. Prolog має кілька дуже коротких кортежних синтаксисів (мій улюблений A-B, але мій другий улюблений - A/Bя використовую тут, оскільки він дає простіший для читання вихід налагодження в цьому випадку); і ми також можемо вибрати власний односимвольний nilдля кінця списку, а не застрягти з двома символами [](я вибрав x, але в основному все працює). Тож замість цього [H|T]ми можемо використовувати T/Hта отримувати вихідb це виглядає приблизно так (зауважте, що порядок сортування кортежів трохи відрізняється від порядку в списках, тому вони не в тому ж порядку, як вище):
x/x/x/x/x
x/x/x/(x/x)
x/(x/x)/(x/x)
x/x/(x/x/x)
x/(x/x/x/x)
x/x/(x/(x/x))
x/(x/x/(x/x))
x/(x/(x/x/x))
x/(x/(x/(x/x)))
Це читати досить важче, ніж вкладені списки вище, але це можливо; подумки пропустити xs і інтерпретувати /()як міхур (або просто звичайний /як вироджений міхур без вмісту, якщо ()після нього немає ), а елементи мають відповідність 1-до-1 (якщо не впорядковано) із наведеною вище списковою версією .
Звичайно, подання цього списку, незважаючи на те, що воно значно скорочене, має великий недолік; він не вбудований у мову, тому ми не можемо використовувати, sort0щоб перевірити, чи відсортований наш список. sort0все-таки є досить багатослівним, однак, це робити вручну не є величезною втратою (насправді, це робити вручну в [H|T]представленні списку доходить приблизно до такої ж кількості байтів). Основне розуміння тут полягає в тому, що програма, як письмові перевірки, перевіряє, чи сортується список, чи сортується його хвіст, чи сортується його хвіст тощо; є багато зайвих перевірок, і ми можемо це використати. Натомість ми просто перевіримо, щоб перші два елементи були в порядку (що гарантує, що список буде впорядкований після того, як перевіряється сам список та всі його суфікси).
Перший елемент легко доступний; це лише голова списку H. Другий елемент доступний, проте, важче, але може і не існувати. На щастя, xце менше, ніж усі кортежі, які ми розглядаємо (через узагальнений оператор порівняння Prolog @>=), тому ми можемо вважати "другим елементом" списку синглтон xі програма буде працювати нормально. Що стосується фактичного доступу до другого елемента, терсест-метод полягає в додаванні до третього аргументу (аргументу з виходу) b, який повертається xв базовому випадку і Hв рекурсивному випадку; це означає, що ми можемо схопити голову за хвіст як вихід з другого рекурсивного дзвінка до B, і, звичайно, голова хвоста є другим елементом списку. Так bвиглядає ось так:
b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
Базовий випадок досить простий (порожній список, повернути кількість 0, "перший елемент" порожнього списку x). Рекурсивний випадок починається так само, як і раніше (тільки з T/Hпозначеннями, а не як [H|T]і Hяк додатковий аргумент); ми нехтуємо додатковим аргументом від рекурсивного дзвінка на голові, але зберігаємо його в Jрекурсивному дзвінку на хвості. Тоді все, що нам потрібно зробити, це переконатися, що Hвоно більше або дорівнює J(тобто "якщо у списку принаймні два елементи, перший більший або дорівнює другому), щоб переконатися, що список закінчується впорядкованим.
На жаль, setofпридатний, якщо ми намагаємось використати попереднє визначення cразом із цим новим визначенням b, оскільки воно розглядає невикористані параметри приблизно так само, як і SQL GROUP BY, що абсолютно не те, що ми хочемо. Можна налаштувати його так, щоб ми хотіли, але ця конфігурація коштує символів. Натомість ми використовуємо findall, яке має більш зручну поведінку за замовчуванням і лише на два символи довше, що дає нам таке визначення c:
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
І ось повна програма; findallпоступово генеруйте шаблони бульбашок, потім витрачайте цілий набір байтів, рахуючи їх (нам потрібно досить довге для перетворення генератора в список, потім, на жаль, багатослівне ім'я, lengthщоб перевірити довжину цього списку, а також панель котла для оголошення функції).