生成Skolem序列


10

Skolem序列

斯科伦序列是序列2n号,其中每个数字i之间1n恰好出现两次,两次出现之间的距离i是准确i的步骤。以下是Skolem序列的一些示例:

1 1
1 1 4 2 3 2 4 3
16 13 15 12 14 4 7 3 11 4 3 9 10 7 13 12 16 15 14 11 9 8 10 2 6 2 5 1 1 8 6 5

以下序列不是 Skolem序列:

1 2 1 2      (The distance between the 1's is 2, not 1)
3 1 1 3      (The number 2 is missing)
1 1 2 1 1 2  (There are four 1's)

目的

编写程序,函数或表达式以计算给定长度的所有Skolem序列的数量。更明确地说,您的输入是整数n,而您的输出是长度为Skolem的序列数2n。该序列具有OEIS条目。为此n = 0,您可以返回01。从开始的前几个值0

0, 1, 0, 0, 6, 10, 0, 0, 504, 2656, 0, 0, 455936, 3040560, 0, 0, 1400156768

规则和计分

这是代码高尔夫。输出格式不合理。


只是好奇,但是0, 1, 0, 0, 6...您的问题是什么?那是代码段,如果是的话,那是什么语言?
PhiNotPi

2
为什么输出中的第一项0?如果您要接受0为有效输入,则输出应为1
彼得·泰勒

1
有些人(包括我的代码)认为空序列为零。如果1使您感觉更好,请返回。
展位,

2
每种情况下,您假设AFAIK 只有一个空序列/空对象/空集等/到/来自空集/空图/其他任何函数。
巴库里

1
@boothby,您只是称呼Knuth是个傻瓜吗?
彼得·泰勒

Answers:


8

GolfScript,48个 46个字符

:b,1,{)2\?){{.2$&!{.2$|@@}*.+.4b?<}do;;}+%}@/,

更快的版本(在线尝试)-运行得相当快,例如n=8大约需要两秒钟。选择的方法几乎不需要字符。

此版本还适用于位掩码。它从1向上构建可能的结果数组,即n=3

1: 000011        000110 001100 011000 110000
2: 010111 101011 101110        011101 110101 111010

虽然某些结果(如000011)具有两个可能的延续,但其他结果(即001100)则没有,并从结果数组中删除。

代码说明:

:b           # save the input into variable b for later use
,            # make the list 0..b-1 (the outer loop)
1,           # puts the list [0] on top of the stack - initially the only possible
             # combination
{)           # {...}@/ does the outer loop counting from i=1 to b
  2\?)       # computes the smalles possible bit mask m=2^i+1 with two bits set 
             # and distance of those equal to i (i.e. i=1: 11, i=2: 101, ...)
  {          # the next loop starts with this bitmask (prepended to code via
             # concatination {...}+
             # the loop itself iterates the top of the stack, i.e. at this point 
             # the result array                 
             # stack here contains item of result array (e.g. 00000011)
             # and bitmask (e.g. 00000101)
    {        # the inner-most loop tries all masks with the current item in the result set
      .2$&!  # do item and result set share not single bit? then - {...}*
      {
        .2$| # then generate the new entry by or-ing those two
        @@   # push it down on the stack (i.e. put working items to top)
      }*
      .+     # shift the bit mask left by one
      .4b?<  # if still in the range loop further
    }do;;    # removes the remainders of the loop (i.e. item processed and mask)
  }+%        # stack now contains the new result array
}@/
,            # length of result array, i.e. the number of Skolem sequences

接受更快的捆绑解决方案。
展位,

6

J表达式,47个字符

 +/*/"1((=&{:+.2-#@])#;.2)\"1~.(i.!+:y)A.,~>:i.y

例:

    y=:5
    +/*/"1((=&{:+.2-#@])#;.2)\"1~.(i.!+:y)A.,~>:i.y
10

y=:5在我的机器上花费大约30秒。

该算法尽可能慢:

  • ~.(i.!+:y)A.,~>:i.y生成的每个排列1 2 .. y 1 2 .. y并删除重复的条目
  • ((=&{:+.2-#@])#;.2)\"1 计算:
    • (...)\"1 每行的每个前缀:
      • #;.2 在最后一个元素的每次出现之前对元素进行计数
      • #@] 计算计数数(即最后一个元素的出现数)
      • =&{: 确定计数列表和原始列表的“最后一个元素”的“相等”。
      • +.是逻辑或。=&{:+.2-#@]读取为“ [计数列表和原始列表]的最后一个元素相等,或者[计数列表]中只有一个元素而不是两个”。
  • */"1 在条件表的行上相乘(逻辑与),确定哪些排列是Skolem序列。
  • +/ 将一和零相加。

6

GolfScript(46个字符)

:&1,\,{0,2@)?)2&*{2${1$^}%@+\2*}*;+}/{4&?(=},,

这是一个需要在堆栈上输入的表达式。要将其变成一个完整的程序,需要在stdin上输入,请添加~

这是相当低效的-我将高尔夫打高尔夫球时从56个未打高尔夫球的高尔夫球场上节省下来的大部分钱都是通过扩大循环范围而实现的,这些循环不会引入错误的结果,但会进行浪费计算。

该方法是笛卡尔积的按位屏蔽。例如,对于n=4非高尔夫代码(使用二进制作为掩码),将计算笛卡尔乘积中每个元素的异或[00000011 00000110 ... 11000000] x [00000101 00001010 ... 10100000] x ... x [00010001 ... 10001000]。8位的任何结果只能通过不重叠的掩码来实现。

为了优化大小而不是速度,该代码会累积部分乘积(S1 u S1xS2 u S1xS2xS3 ...),并使每个乘积都包含2n元素,而不仅仅是2n-1-i可能真正有助于有效序列的。

速度

打高尔夫球的版本n=5在我的计算机上运行10秒钟,而在上运行5分钟以上n=6。原始的非高尔夫版本的计算n=5时间不到一秒钟,n=6大约需要1分钟。通过简单的中间结果过滤器,它可以n=8在30秒内完成计算。我将其打高尔夫球至66个字符(作为程序-65个表达式作为表达式),同时保持循环尽可能地受限制并过滤中间碰撞:

~:&1,\,{0,\).2\?)2&*@-{.{[\].~^.@~+<{;}*}+3$%@+\2*}*;\;}/{4&?(=},,

该死的。就在我认为我的48char J解决方案足够好可以发布的时候。
John Dvorak 2013年

该死的。我们的47个字符的领带持续时间不长。+1
约翰·德沃夏克2013年

5

GolfScript,49个字符

~:/..+?:d(,{d+/base(;:w;/,{.w?)w>1$?=},,/=},,/1=+

期望nSTDIN上的数字。这是代码高尔夫球-不要尝试n大于5 的代码。


哦,不超过5吗?
展位,2013年

@boothby这是第一次直接尝试。我们经常不得不考虑决策速度与大小的关系-代码高尔夫球的大小就是大小。这就是为什么我还添加了快速版本的原因-该版本原本要长得多,但现在更短。
霍华德

0

贤者,70

这比我原来的要短。

sum(1for i in DLXCPP([(i-1,j,i+j)for i in[1..n]for j in[n..3*n-i-1]]))

怎么运行的:

给定一个0/1矩阵,该矩阵的确切覆盖问题是找到总和(作为整数)与全1向量的行的子集。例如,

11001
10100
01001
00011
00010

有解决方案

10100
01001
00010

我最喜欢的解决问题的方法是将它们转换为确切的掩盖问题。Skolem序列有效地促进了这一点。我提出了一个精确的掩盖问题,其中解决方案与Skolem长度序列成为双射2n。例如,针对的一行问题n=6

  a   |  b  
001000|001001000000 # S[b] = S[b+a+1] = a

其中位置1 a < n表示使用该符号a。其余位置对应于序列中的实际位置。精确的封面对应于每个符号仅被使用一次,并且每个位置仅被填充一次。通过构造,k位置中的任何符号都k与其伙伴保持空白。

在Sage中,DLXCPP是一个“跳舞链接”实现-它以格外优雅的方式解决了确切的遮盖问题。它曾经是我最喜欢的算法之一,而就在Sage的表面上,组合枚举是一种乐趣。


哇,跳舞的环节。使用len(list(...))将节省4个字符。
2013年

@Ray如果我计算len(list(...))n = 16,我的计算机就会死掉。并且它会完全杀死运行时。
展位,2013年

没错,因为将生成器转换为列表会占用大量内存。
2013年
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.