Плитка доміно в Фібоначчі


11

Існує класичний комбінаторний результат, згідно з яким кількість способів плитки 2*nсмуги 1*2доміно є n- м числом Фібоначчі. Ваша мета - роздрукувати всі обшивки для даної n, намальовані штрихами та вертикальними лініями, як ці 8 обрешіток для n=5:

|————
|————

——|——
——|——

|||——
|||——

————|
————|

||——|
||——|

|——||
|——||

——|||
——|||

|||||
|||||

Ви повинні надати програму або названу функцію, яка приймає nяк введення та друкує необхідний вихід. Виграє найменше байт.

Вхідні дані

Число nміж 1та 10включно через STDIN або функціональний вхід.

Вихідні дані

Роздрукуйте всі можливі обшивки доміно до 2*nсмужки, намальовані горизонтально. Обшивка може бути в будь-якому порядку, але кожна повинна з’являтися рівно один раз. Вони повинні бути розділені порожнім рядком.

Вертикальне доміно складається з двох вертикальних брусків ( |), а горизонтальне доміно - з двох тире ( ). Ви можете використовувати дефіси ( -) замість тире, щоб залишитися в ASCII.

З пробілом можна робити все, що завгодно, поки надрукований вихід виглядає однаково.


Чи буде додаткова подача ліній після останньої плитки потрапляти під що- небудь із пробілом ?
Денніс

@Dennis Так, додаткові порожні рядки добре.
xnor

Я дуже здивований, коли бачу так багато різних підходів до вирішення однієї проблеми. Ти цього очікував?
Рівень річки Св.

@steveverrill Ні, я зовсім цього не зробив, і я радий бачити різноманітність! А твій приймає торт за несподіваність. Я здебільшого мав на увазі рекурсію в стилі Фібоначчі, як і більшість використовуваних рішень. Я не очікував , що фільтрація , щоб бути ефективним, і в тій мірі , яку я зробив, я хоча б фільтрувати рядки ——і |від довжини , як Денніс, а НЕ довжина- nрядки і |фільтрується з'являються парами. І для останнього, я б очікував, що це буде через регулярні вирази або рядкові операції на створеній рядку, як-от s.split('——) `, а не за допомогою арифметичного підходу, як ваш.
xnor

Я думаю, що "1x2 доміно" є зайвим.
SuperJedi224

Відповіді:


5

С, 106

Гольф-версія

f(n){
  for(int i,h=n*2<<n;h--;(i/3|i/3*2)-i||(putchar(i>>h%n&1?45:124),h%n||puts(h%(n*2)?"":"\n")))
    i=h/2/n;
}

Оригінальна версія

i,j,n;
main(){
  scanf("%d",&n);
  for(i=1<<n;i--;)if((i/3|i/3*2)==i){
    for(j=1<<n;j/=2;)printf("%c",i&j?'-':'|');puts("");
    for(j=1<<n;j/=2;)printf("%c",i&j?'-':'|');puts("\n");
  }
}

Як це працює

Змінна iпрацює від 1<<n-1нижнього до 0, створюючи всі можливі двійкові числа з nцифрами. 0 кодує |і 1 кодує для -. Нас цікавлять числа, які містять 1 у парах. Очевидно, що такі числа поділяються на 3.

Коли число ділиться на 3, вихідне число можна відновити, помноживши результат на 2 і додавши його до себе (ефективно змішуючи на 3.) Більшість номерів потребує перенесення, але коли процес здійснюється на числа відсоток, перенос не потрібен, тому лише в цих випадках АБО можна використовувати замість додавання. Це використовується для перевірки на кількість цікавлять, оскільки вони є єдиними, де вираз i/3|i/3*2повертає початкове значення i. Дивіться приклад нижче.

1111= 15 ---> 0101= 5 ---> 1111= 15 (дійсно, 0101|1010== 0101+1010)

1001= 9 ---> 0011= 3 ---> 0111= 7 (недійсне 0011|0110,! = 0011+0110)

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

У оригінальній версії пара циклів jвикористовується для сканування бітів iта отримання виводу. У гольф-версії використовується один forцикл, який hпроходить через усі числа від (n*2)*(1<<n)-10 до 0. Значення iпороджуються h/2/n. Змінна jбільше не використовується, оскільки еквівалентна кількість отримана з h%n. Використання n*2дозволяє обидва рядки друкувати з одного циклу, з деяким чудовим мультиплексуванням у putsоператорі друкувати або один, або два нові рядки в кінці рядка.

Зверніть увагу , що м'ясо це в положенні збільшення for()кронштейна і , отже , запускається на виконання післяi=h/2/h .

Вибірка вибірки n = 6:

$ ./a
6
------
------

----||
----||

--|--|
--|--|

--||--
--||--

--||||
--||||

|----|
|----|

|--|--
|--|--

|--|||
|--|||

||----
||----

||--||
||--||

|||--|
|||--|

||||--
||||--

||||||
||||||

i/3|i/3*2Трюк геніальний! Я не очікував арифметичного вираження для граматики.
xnor

3

CJam, 33 27 байт

LN{_'|f+@"——"f++}ri*\;{_N}/

Дякуємо @ jimmy23013 за те, що ти граєш на 6 байт!

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

Фон

Це ітеративна реалізація рекурсивного алгоритму:

Можливі нахили для n можна отримати, додавши вертикальні доміно до можливих обрізків для n - 1 і два горизонтальних доміно до можливих нахилів для n - 2 .

Таким чином, кількість нахилів для n є сумою чисел нахилів для n - 1 і n - 2 , тобто n- го числа Фібоначчі.

Як це працює

LN                                " A:= [''] B:= ['\n']                         ";
  {             }ri*              " Repeat int(input()) times:                  ";
   _'|f+                          "   C = copy(B); for T ∊ C: T += '|'          ";
              @                   "   Swap A and B.                             ";
               "——"f+             "   for T ∊ B: T += "——"                      ";
                     +            "   B = C + B                                 ";
                        \;        " Discard A.                                  ";
                          {_N}/   " for T ∊ B: print T, T + '\n'                ";

Приклад виконання

$ alias cjam='java -jar cjam-0.6.2.jar'

$ cjam domino.cjam <<< 3
|||
|||

——|
——|

|——
|——

$ for i in {1..10}; do echo $[$(cjam domino.cjam <<< $i | wc -l) / 3]; done1
2
3
5
8
13
21
34
55
89

LNli{_'|f\@"——"f\+2/}*\;{_N}/.
jimmy23013

f\ще не було впроваджено в 0.6.2, але я зміг поєднати наші підходи. Дякую!
Денніс

2

Haskell, 89 байт

f(-1)=[]
f 0=[""]
f n=map('|':)(f$n-1)++map("--"++)(f$n-2)
g=unlines.(>>= \x->[x,x,""]).f

f- це функція, яка задає число, повертає список з одних рядків усіх можливих нахилів Фібоначчі довжиною n можливих. не має значення, що він повертає одну лінію, тому що обидві лінії всіх нахилів однакові.

fпрацює за допомогою рекурсивного виклику на n-1та n-2додавання "|"та "--"(відповідно) до рядків.

gце функція, яка відповідає на запитання. він в основному викликає fвхід, подвоює кожну рядок, щоб вона відображала два рядки та з'єднувала їх усі новими рядками.

Приклад виведення:

*Main> putStrLn $ g 5
|||||
|||||

|||--
|||--

||--|
||--|

|--||
|--||

|----
|----

--|||
--|||

--|--
--|--

----|
----|

2

CJam, 42 37 байт

3li:L#,{3b"——|"2/f=s}%{,L=},_&{N+_N}/

Я перерахував тире як один байт кожен, оскільки питання дозволяє замінити їх дефісами ASCII.

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

Як це працює

Для отримання всіх можливих нахилів 2 × L ми повторюємо всі невід’ємні цілі числа << L , при цьому парні цифри в базі 3 відповідають горизонтальним доміно, а непарні цифри - вертикальним.

Так як кожен я маю L або менше цифр в підставі 3, це породжує всі можливі способи покриття L 2 × смуги. Залишилося лише відфільтрувати покриття, які перевищують 2 × L, і надрукувати решту покриттів.

3li:L#,      " Read an integer L from STDIN and push A := [ 0 1 ... (3 ** N - 1) ].       ";
{            " For each integer I in A:                                                   ";
  3b         " Push B, the array of I's base 3 digits.                                    ";
  "——|"2/    " Push S := [ '——' '|' ].                                                    ";
  f=         " Replace each D in B with S[D % 2] (the modulus is implicit).               ";
  s          " Flatten B.                                                                 ";
}%           " Collect the result in an array R.                                          ";
{,L=},       " Filter R so it contains only strings of length L.                          ";
_&           " Intersect R with itself to remove duplicates.                              ";
{N+_N}/      " For each string T in B, push (T . '\n') twice, followed by '\n'.           ";

Приклад виконання

$ cjam domino.cjam <<< 3
|——
|——

——|
——|

|||
|||

$ for i in {1..10}; do echo $[$(cjam domino.cjam <<< $i | wc -l) / 3]; done
1
2
3
5
8
13
21
34
55
89

Класно. Мені просто цікаво, чому ви не використовували базу 2 типу edc65 замість бази 3. Це врятувало б вас від копій. Я припускаю, що тому, що провідні нулі, ймовірно, обрізаються на кроці 3b. Це так?
Рівень р. Св.

1
@steveverrill: Так, саме в цьому причина. Як і в даний час, провідні нулі взагалі не відповідають доміно. Але відсутність дублікатів дозволить мені замінити три блоки на один. Мені доведеться подумати над цим ще трохи.
Денніс

@steveverrill: Мені не вдалося змусити роботу базової 2, але рекурсивний підхід здається ще коротшим.
Денніс

2

JavaScript (E6) 102

Створіть конфігурації з бітових послідовностей, 0 -> '-' та 1 -> '|'.

F=n=>{
  for(i=0;(j=++i)<2<<n;s.length==1+n&&console.log('\n'+s+s))
    for(s='\n';j>1;j>>=1)s+=j&1?'|':'--';
}

Тест у консолі firefox / firebug

F(5)

Вихідні дані

|----
|----

--|--
--|--

----|
----|

|||--
|||--

||--|
||--|

|--||
|--||

--|||
--|||

|||||
|||||

1

Haskell: 109 байт

Це переклад відомої однолінійки Haskell для лінивого обчислення послідовності чисел Фібоначчі:

b=map.map.(++)
w=zipWith
z=w(++)
s=["\n"]:["|\n"]:z(b"--"s)(b"|"$tail s)
f=sequence_.map putStrLn.(w z s s!!)

Основна послідовність плитки рядків, що не входять в гольф:

dominos = [""] : ["|"] : zipWith (++) ("--" `before` dominos) ("|" `before` tail dominos)
    where before = map . map . (++)

І однолінійка Фібоначчі для порівняння:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

Приклад використання:

$ ghci fibtile
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling Main             ( fibtile.hs, interpreted )
Ok, modules loaded: Main.
*Main> f 5
----|
----|

--|--
--|--

--|||
--|||

|----
|----

|--||
|--||

||--|
||--|

|||--
|||--

|||||
|||||

*Main>

1

Кобра - 176

Не можу зачекати, поки я закінчу пакет для гри в гольф Cobra.

def m(n)
    for t in.f(n),print t+t
def f(n,x='')as String*
    if x.length<n,for i in.f(n,x+'-').toList+.f(n,x+'|').toList,yield i
    else if not'-'in x.replace('--',''),yield x+'\n'

1

J - 54 char

Функція, яка бере nаргумент справа.

0{::(];'--'&,"1@[,'|',"1])&>/@[&0&((1 2$,:)&.>'';,'|')

Основний корінь цього гольфу - це (];'--'&,"1@[,'|',"1])&>/. Це займає перелік обрізів довжини (N-2) і (N-1) і повертає перелік обрізів довжини (N-1) і N. Це стандартний повторення Фібоначчі, лінійна алгебра. ];повертає правий список як новий лівий (оскільки змін немає). '--'&,"1@[додає --плитки до лівого списку, тоді як '|',"1]додає |плитки до правого списку, а ті разом - новий правий список.

Ми повторюємо це раз і знову n(саме це @[&0) і починаємо з порожньої плитки та одинарної плитки довжиною 1. Потім повертаємо першу з пари 0{::. Значить, якщо ми запустимо його нуль разів, ми просто повернемо першу, тобто порожню плитку. Якщо ми будемо виконувати його в nрази, ми обчислюємо до nта ( n+1) пари, але відкидаємо останнє. Це зайва робота, але менше символів.

(1 2$,:)Що - то J повинен зробити для того , щоб зробити Тайлінг в списках легко розширюється. Ми робимо лівий стартовий список 1-позиційним списком дворядкової матриці символів, кожен рядок має довжину 0. Правий стартовий список такий самий, але рядки мають довжину 1, заповнені |. Потім ми додаємо нові плитки до кожного ряду та додаємо списки матриць, коли ми з'єднуємо два набори окантовки разом. Це просте застосування концепції J call rank: по суті маніпулює розмірністю своїх аргументів, і неявно циклічно, коли це необхідно.

   0{::(];'--'&,"1@[,'|',"1])&>/@[&0&((1 2$,:)&.>'';,'|')5
----|
----|

--|--
--|--

--|||
--|||

|----
|----

|--||
|--||

||--|
||--|

|||--
|||--

|||||
|||||

Спробуйте самі на tryj.tk .


1

Пітон 3: 70 байт

f=lambda n,s="\n":n>0and f(n-1,"|"+s)==f(n-2,"--"+s)or n or print(s*2)

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

==Між двома викликами для fтільки для виконання обох викликів функцій. Зазвичай Noneвони повертаються, оскільки вони просто друкують, і ==є одним з небагатьох операторів, визначених для None.

В ands і ors є зробити правильну поведінку коротке замикання , щоб відтворити ifз і elseх років ungolfed коду.

Безголівки:

def f(n,s="\n"):
 if n==-1:pass
 elif n==0: print(s*2)
 else: f(n-1,"|"+s);f(n-2,"--"+s)

1

Сітківка , 44 байти

Примітка: Сітківка молодша за цей виклик.

+`([-|]*)11(1*#)
$1|1$2$1--$2
1
|
.*?#
$0$0#

Здійснює введення в одинаковому порядку із заднім рядком.

Кожен рядок повинен мати власний файл і його #слід змінити на новий рядок у файлі. Це недоцільно, але ви можете запускати код як один файл із -sпрапором, зберігаючи #маркери (і змінюючи також новий рядок на #вхід). Ви можете змінити #повернення назад на нові рядки у висновку для зручності читання, якщо бажаєте. Наприклад:

> echo 1111# | retina -s fib_tile | tr # '\n'
||||
||||

||--
||--

|--|
|--|

--||
--||

----
----

Спосіб:

  • Починаючи з введення, ми поміняємо кожен рядок двома іншими: один із першою 1зміною на |та один із першими двома 1зміненими на --. Ми робимо це, поки у нас є рядки з принаймні двома 1.
  • Коли 1залишилися лише сингли , ми змінили їх на |.
  • Ми подвоюємо кожен рядок і додаємо до нього додатковий новий рядок і отримуємо потрібний вихід.

Новий рядок - це добре.
xnor
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.