斐波那契多米诺骨牌


11

经典的组合的结果,一个办法瓦数2*n由带1*2多米诺骨牌是第n Fibonacci数。您的目标是打印给定的所有拼贴n,并用虚线和垂直线绘制,例如以下8个拼贴n=5

|————
|————

——|——
——|——

|||——
|||——

————|
————|

||——|
||——|

|——||
|——||

——|||
——|||

|||||
|||||

您将提供一个程序或命名函数n作为输入并打印所需的输出。最少的字节数获胜。

输入值

通过STDIN或功能输入n之间的数字110含)。

输出量

打印2*n水平绘制的每个可能的多米诺骨牌瓷砖。平铺可以以任何顺序进行,但每个平铺应恰好出现一次。它们必须用空白行分隔。

垂直多米诺骨牌由两个垂直条(|)组成,水平多米诺骨牌由两个破折号()组成。您可以使用连字符(-)代替破折号以保持ASCII。

只要打印输出看起来相同,您就可以对空格进行任何操作。


在最后一个平铺之后,额外的换行符会落在带有空格的任何内容下吗?
丹尼斯2014年

@Dennis是的,多余的空白行可以。
xnor 2014年

看到如此多种解决相同问题的方法,我感到非常惊讶。你有期望吗?
水平河圣

@steveverrill不,我完全没有,很高兴看到这个品种!而您的举动却出乎意料。正如我使用的大多数解决方案一样,我大多想到的是斐波那契式的递归。没想到过滤是有效的,并且我做到了,我虽然它会被过滤的字符串的程度——,并|通过丹尼斯一样的,而不是长度-长度n的字符串,并|通过过滤出现在对。对于后者,我希望它是通过正则表达式或对生成的字符串(例如s.split('——)` 进行的字符串操作,而不是通过像您这样的算术方法。
xnor 2014年

我认为“ 1x2多米诺骨牌”是多余的。
SuperJedi224

Answers:


5

C,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");
  }
}

这个怎么运作

变量i1<<n-1下到下运行,产生所有可能的带有n数字的二进制数。0编码|,1编码-。我们对包含成对的1的数字感兴趣。显然,这些数字可以被3整除。

当数字除以3时,可以通过将结果乘以2并将其加到自身(有效地乘以3)来恢复原始数字。大多数数字都需要带进位,但是当对兴趣,不需要进位,因此仅在这些情况下,可以使用OR代替加法。这是用于测试感兴趣的数字,因为它们是表达式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时也返回小于原始数的数字,因此测试也对这些数字给出了所需的FALSE。

在原始版本中j,使用了两个循环来扫描的位i并产生输出。在高尔夫球版本中,使用单个for循环,其中h(n*2)*(1<<n)-1低到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 2014年

3

CJam,33 27字节

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

感谢@ jimmy23013高尔夫球了6个字节!

在线尝试!

背景

这是递归算法的迭代实现:

n的可能平铺可通过将垂直多米诺骨牌添加到n-1的可能平铺,并将两个水平的多米诺骨添加到n-2的可能平铺而获得。

这样,n的平铺数是n-1n-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 '16

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的所有可能的平铺,我们对所有非负整数I <3 L进行迭代,使以3为底的偶数位对应于水平多米诺骨牌,而奇数位对应于垂直多米诺骨牌。

由于每个I在基数3中都具有L个或更少的数字,因此生成了覆盖2×L条形的所有可能方式。剩下的就是过滤掉大于或小于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

凉。我只是想知道为什么您不使用像edc65这样的基数2而不是基数3。这将使您免于重复。我认为这是因为前导零可能会在步骤中被截断3b。那正确吗?
水平河圣

1
@steveverrill:是的,这就是原因。实际上,前导零根本就没有多米诺骨牌。但是没有重复将使我可以用一个替换三个块。我将不得不再考虑一下。
丹尼斯

@steveverrill:我没有设法使base 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

等不及我完成了眼镜蛇高尔夫套餐。

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个字符

函数以n右侧为参数。

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

这种高尔夫的主要根源是(];'--'&,"1@[,'|',"1])&>/。这将获取一个长度为(N-2)和(N-1)的平铺列表,并返回一个长度为(N-1)和N的平铺列表。这是标准的斐波纳契递归,线性代数。];返回右边的列表作为新的左边(因为没有变化)。'--'&,"1@[--图块添加到左侧列表中,而'|',"1]|图块添加到右侧列表中,这些都一起构成了新的右侧列表。

我们反复遍历该n次数(即@[&0),然后从空切片和长度为1的单个切片开始。然后,用返回返回配对中的第一个0{::。意思是,如果我们运行零次,则只返回第一个,即空拼贴。如果我们运行它n一次,我们最多计算nand(n+1)对,但丢弃后者。这是额外的工作,但字符较少。

(1 2$,:)东西j之后,为了使在列表中的拼接容易扩展做。我们将左边的开始列表设为2行字符矩阵的1个项目列表,每行的长度为0。右边的开始列表相同,但行的长度为1,并用填充|。然后,我们将新的图块添加到每一行,并在将两组图块结合在一起时追加矩阵列表。这是J调用rank的概念的简单应用:本质上是操纵其参数的维数,并在必要时隐式循环。

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

--|--
--|--

--|||
--|||

|----
|----

|--||
|--||

||--|
||--|

|||--
|||--

|||||
|||||

tryj.tk上尝试一下。


1

Python 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为产生正确的短路行为重现ifS和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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.