Гольфскрипт - 56 50 49 48 41 40 38 37 символів
n%{~),{!}%\{0.@{.@+2$*@)@}/;;]}*)p;}/
Примітка: це обробляє кілька рядків введення, швидко (1/8 сек для тестових випадків) і не порушується для жодного юридичного введення.
(Перша версія була також моєю першою в історії програмою Golfscript; завдяки електронному бізнесу було вказано кілька хитрощів, які я пропустив).
Щоб зробити це також корисним навчальним дописом, ось пояснення того, як це працює. Починаємо з рецидиву f(n, k) = k * (f(n-1, k) + f(n-1, k-1))
. Це можна зрозуміти комбінаторно, як кажучи, що для розміщення n
помітних кульок у k
відрізних відрах таким чином, щоб кожне відро містило щонайменше одну кульку, ви вибираєте одне із k
відра для першої кулі ( k *
) і тоді або вона буде містити принаймні ще одну кулю ( f(n-1, k)
) або не буде ( f(n-1, k-1)
).
Значення, отримані внаслідок цього, утворюють сітку; приймаючи n
як індекс рядка і k
як індекс стовпця та індексуючи обидва з 0, він починається
1 0 0 0 0 0 0 ...
0 1 0 0 0 0 0 ...
0 1 2 0 0 0 0 ...
0 1 6 6 0 0 0 ...
0 1 14 36 24 0 0 ...
0 1 30 150 240 120 0 ...
0 1 62 540 1560 1800 720 ...
. . . . . . . .
. . . . . . . .
. . . . . . . .
Тож звертаючись до програми,
n%{~ <<STUFF>> }/
розбиває введення на рядки, а потім для кожного рядка оцінює його, ставлячи n
і k
в стек, а потім викликає <<STUFF>>
, що таке:
),{!}%\{0.@{.@+2$*@)@}/;;]}*)p;
Тут обчислюються перші k+1
записи першого n+1
ряду цієї сітки. Спочатку стек є n k
.
),
дає стек n [0 1 2 ... k]
{!}%
give стек того, n [1 0 0 ... 0]
де є k
0s.
\{ <<MORE STUFF>> }*
підводить n
до вершини і робить це кількість разів, коли ми виконуємо <<MORE STUFF>>
.
Наш стек на даний момент є рядком таблиці: [f(i,0) f(i,1) ... f(i,k)]
0.@
ставить пару 0 перед цим масивом. Перший буде, j
а другий буде f(i,j-1)
.
{ <<FINAL LOOP>> }/
петлі через елементи масиву; для кожного з них він ставить його поверх стека, а потім виконує тіло циклу.
.@+2$*@)@
нудна маніпуляція стеком, щоб взяти ... j f(i,j-1) f(i,j)
та ... j*(f(i,j-1)+f(i,j)) j+1 f(i,j)
;;]
випустити спливи зліваk+1 f(i,k)
і збирає все в масив, готовий до наступного обходу циклу.
Нарешті, коли ми створили n
третій рядок таблиці,
)p;
бере останній елемент, друкує його та відкидає решту рядка.
Для нащадків три принципові рішення за цим принципом:
n%{~),{!}%\{0.@{.@+@.@*\)@}/;;]}*)p;}/
n%{~),{!}%\{0:x\{x\:x+1$*\)}/;]}*)p;}/
n%{~),{!}%\{0.@{@1$+2$*\@)}/;;]}*)p;}/