蠕虫的生命


28

条款

一个蠕虫是负整数的任何名单,以及其最右边(即最后一个)元素称为。如果头部不为0,则蠕虫的活动段最长的连续元素块组成,该元素块包括头部,并且其所有元素至少与头部一样大。在简约有效段是主动段与由1。例如递减的头,所述蜗杆3 1 2 3 2具有活性片段2 3 2,以及减少的活性段2 3 1

进化规则

蠕虫逐步演化如下:

在步骤t(= 1,2,3,...)中,
    如果头为0:删除头,
    否则:用缩减的活动段的t + 1级联副本替换活动段。

事实任何蠕虫最终都会演变成空列表,而这样做的步骤就是蠕虫的生存期

(有关详细信息,请参阅LD Beklemishev撰写的《蠕虫原理》。“列表”是指有限序列,“头”是指最后一个元素的用法,摘自本文—请勿混淆)并将列表作为抽象数据类型常用,其中head通常表示第一个元素。)

示例(括号中的活动部分)

蠕虫:0,1

step    worm
         0(1)
1        0 0 0
2        0 0 
3        0
4           <- lifetime = 4

蠕虫:1,0

step    worm
         1 0
1       (1)
2        0 0 0
3        0 0 
4        0
5           <- lifetime = 5

蠕虫:1,1

step    worm
        (1 1)
1        1 0 1 0 
2        1 0(1) 
3        1 0 0 0 0 0
4        1 0 0 0 0
5        1 0 0 0
...
8       (1) 
9        0 0 0 0 0 0 0 0 0 0
10       0 0 0 0 0 0 0 0 0
...
18       0
19           <- lifetime = 19

蠕虫:2

step    worm
        (2)
1       (1 1)
2        1 0 1 0 1 0
3        1 0 1 0(1)
4        1 0 1 0 0 0 0 0 0
5        1 0 1 0 0 0 0 0
6        1 0 1 0 0 0 0
...
10       1 0(1)
11       1 0 0 0 0 0 0 0 0 0 0 0 0 0
12       1 0 0 0 0 0 0 0 0 0 0 0 0
...
24      (1)
25       0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
...
50       0
51          <- lifetime = 51

蠕虫:2,1

        (2 1)
1        2 0 2 0
2        2 0(2)
3        2 0(1 1 1 1)
4        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0
5        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0(1 1 1)
6        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0
7        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0(1 1)
8        2 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0{1 0}^9
...
??          <- lifetime = ??      

蠕虫:3

step    worm
        (3)
1       (2 2)
2       (2 1 2 1 2 1)
3        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 
4        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1(2)
5        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0(2 1 2 1 1 1 1 1 1 1)
6        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0{2 1 2 1 1 1 1 1 1 0}^7
7        2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0{2 1 2 1 1 1 1 1 1 0}^6 (2 1 2 1 1 1 1 1 1) 
...      ...
??          <- lifetime = ??


在旁边

蠕虫的寿命通常是巨大的,如在标准的条件通过如下所示的下界快速增长的层次结构的函数f α

worm                lower bound on lifetime
----------------    ------------------------------------------
11..10 (k 1s)       f_k(2)
2                   f_ω(2)
211..1 (k 1s)       f_(ω+k)(2)
2121..212 (k 2s)    f_(ωk)(2)
22..2 (k 2s)        f_(ω^k)(2)
3                   f_(ω^ω)(2)
...
n                   f_(ω^ω^..^ω)(2) (n-1 ωs)  >  f_(ε_0) (n-1)

值得注意的是,蠕虫[3]的寿命已经远远超过了格雷厄姆数 G:

˚F ω ω(2)= F ω 2(2)= F ω2(2)= F (ω)+ 2(2)= F (ω)+ 1(F (ω)+ 1(2))>> F (ω)+ 1(64) >G。


高尔夫挑战赛

编写具有以下行为的最短函数子程序:

输入:任何蠕虫。
输出:蠕虫的生存期。

代码大小以字节为单位。


这是一个示例(Python,可读取约167个字节):

from itertools import *
def T(w):
    w=w[::-1]
    t=0
    while w:
        t+=1
        if w[0]:a=list(takewhile(lambda e:e>=w[0],w));a[0]-=1;w=a*(t+1)+w[len(a):]
        else:w=w[1:]
    return t


注意:如果t(n)是蠕虫的生存期[n],则t(n)的增长率大约是Goodstein函数的增长率。因此,如果可以将其压缩到100字节以下,则可以很好地回答“ 最大数字可打印”问题。(对于该答案,可以通过始终将步数计数器始终从n开始(与蠕虫[n]相同的值)而不是从0开始,来大大提高增长率。)


我对您的代码感到困惑。您说head是最右边的元素,但是在您的Python示例中,您将head视为w[0]那个列表的*最左边的元素?

@LegoStormtroopr如果可以将列表视为左右两边。如果仅考虑第一个和最后一个,则可以在读取初始字符串时将最右边的内容映射到第一个或最后一个-这不是问题的一部分。但是函数输入也没有严格定义。
鲍勃

@LegoStormtroopr-好收获;我通过添加一行以反转输入蠕虫来纠正代码,该蠕虫的头部确实应该在右侧(即列表w中的最后一个元素)。该程序在反向蠕虫上运行是为了提高效率。
res

得到正确答案2 1可能是过分的要求在合理的时间,但一个有用的测试是顺序应该开始(2 1)2 0 2 02 0 (2)2 0 (1 1 1 1),...
彼得·泰勒

1
@ThePlasmaRailgun-用哈维·弗里德曼(Harvey Friedman)来解释,与TREE(3)相比,在快速增长的层次结构中从ε_0级别的函数得出的数字(例如蠕虫生存时间)是完全无法识别的
水库

Answers:


15

GolfScript(56 54个字符)

{-1%0\{\)\.0={.0+.({<}+??\((\+.@<2$*\+}{(;}if.}do;}:L;

在线演示

我认为这里的关键技巧可能是使蠕虫保持相反的顺序。这意味着找到活动段的长度非常紧凑:(.0+.({<}+??在其中0添加作为保护,以确保我们找到一个小于头部的元素)。


顺便说一句,蠕虫寿命的一些分析。我将蠕虫表示为age, head tail(即,与问题符号相反的顺序),使用指数表示头部和尾部的重复:例如2^3is 2 2 2

引理(Lemma):对于任何活动段xs,都有一个f_xs可以age, xs 0 tail转换为的函数f_xs(age), tail

证明:任何活动句段都不能包含a 0,因此到我们删除尾部之前的所有内容时的年龄与尾部无关,因此仅是的函数xs

引诱:对于任何活跃的部分xs,蠕虫都会age, xs在死亡时死亡f_xs(age) - 1

证明:由上一个引理age, xs 0转化为f_xs(age), []。最后一步是删除that 0,这是以前没有涉及的,因为它永远无法形成活动段的一部分。

通过这两个引理,我们可以研究一些简单的活动段。

对于n > 0

age, 1^n 0 xs -> age+1, (0 1^{n-1})^{age+1} 0 xs
              == age+1, 0 (1^{n-1} 0)^{age+1} xs
              -> age+2, (1^{n-1} 0)^{age+1} xs
              -> f_{1^{n-1}}^{age+1}(age+2), xs

因此f_{1^n} = x -> f_{1^{n-1}}^{x+1}(x+2)(使用base case f_{[]} = x -> x+1,或者如果您愿意f_{1} = x -> 2x+3)。我们看到Ackermann–Péter函数f_{1^n}(x) ~ A(n+1, x)在哪里A

age, 2 0 xs -> age+1, 1^{age+1} 0 xs
            -> f_{1^{age+1}}(age+1)

这足以解决问题1 22 1在问题的表示法中):

1, 1 2 -> 2, 0 2 0 2
       -> 3, 2 0 2
       -> f_{1^4}(4), 2
       -> f_{1^{f_{1^4}(4)+1}}(f_{1^4}(4)+1) - 1, []

因此,给定输入,2 1我们期望输出〜A(A(5,4), A(5,4))

1, 3 -> 2, 2 2
     -> 3, 1 2 1 2 1 2
     -> 4, 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2
     -> 5, 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2 0 2 1 2 1 2
     -> f_{21212}^4(5) - 1

age, 2 1 2 1 2 -> age+1, (1 1 2 1 2)^{age+1}
               -> age+2, 0 1 2 1 2 (1 1 2 1 2)^age
               -> age+3, 1 2 1 2 (1 1 2 1 2)^age

我真的可以理解为什么这个功能如此疯狂地增长了。


很酷。我认为该程序也将为最短终止程序的胜出答案,该程序的输出大小超过Graham的数字。(当前的获胜者有63字节的Haskell代码。)例如,在55字节时,类似(由于我容易出现语法错误)9{-1%0\{\)\.0={.0+.({<}+??\((\+.@<2$*\+}{(;}if.}do;}:L~计算蠕虫的生存期[9],该生存期远远超过了Graham的数量-并且可以是打高尔夫球。
2014年

9

GolfScript,69 62个字符

{0:?~%{(.{[(]{:^0=2$0+0=<}{\(@\+}/}{,:^}if;^?):?)*\+.}do;?}:C;

该函数C期望蠕虫在堆栈上,并用结果替换它。

例子:

> [1 1]
19

> [2]
51

> [1 1 0]
51

太棒了!当然,您可以对此进行一些修改,以肯定地回答“可打印的最大数量”问题。
2014年

我没有看到您在那儿发布任何内容,所以我继续发布了此代码的修改内容,作为到目前为止我认为是成功的答案-假设the *^not未被用作乘法的算术运算符并求幂 当然,如果您想在那里提交自己的答案(无疑是上等的),我会很乐意删除我的答案。
2014年

7

Ruby-131个字符

我知道这不能与上面的GolfScript解决方案竞争,而且我很确定可以将分数降低或减少更多字符,但是老实说,我很高兴能够解决这个难题。大难题!

f=->w{t=0;w.reverse!;until w==[];t+=1;if w[0]<1;w.shift;else;h=w.take_while{|x|x>=w[0]};h[0]-=1;w.shift h.size;w=h*t+h+w;end;end;t}

从上面推导出的我的预解决方案:

def life_time(worm)
  step = 0
  worm.reverse!
  until worm.empty?
    step += 1
    if worm.first == 0
      worm.shift
    else
      head = worm.take_while{ |x| x >= worm.first }
      head[0] -= 1
      worm.shift(head.size)
      worm = head * (step + 1) + worm
    end
  end
  step
end

通用提示:许多高尔夫球问题都适用于非负整数,在这种情况下if foo==0可以修整为if foo<1。这样可以在这里节省一个字符。
彼得·泰勒

顺便说一句,我觉得这很有趣,无需花一秒钟reverse
彼得·泰勒

啊,不是。它仅适用于测试用例,因为它们仅具有回文式活动段。
彼得·泰勒

感谢高尔夫技巧,@ PeterTaylor。同样,在错过的第二次逆转上获得不错的收获。我已经添加了它。稍后,我将尝试以另一种方式重写这种方式,而无需使用反向。我很确定我可以将else子句简化为一行,然后将换成if..else..end三元语句。我认为我也可以使用lambda保存一些字符。
OI 2014年

6

滑动(43个字符)

글坼가⑴감套擘終長①加⒈丟倘⓶增⓶가采⓶擘❷小終⓷丟❶長貶❷가掊貶插①增復合감不가終終

这期望输入为以空格分隔的列表。这会为1 1和输出正确的答案2,但是对于2 13来说花费的时间太长,所以我放弃了等待它完成的过程。

有评论:

글坼 | split at spaces
가⑴ | iteration count = 0

감套 | while:
  擘終長①加⒈丟 | remove zeros from end and add to iteration count
  倘 | if the list is not empty:
    ⓶增⓶ | increment iteration count
    가采⓶擘❷小終⓷丟 | separate out active segment
    ❶長貶❷가掊貶插 | compute reduced active segment
    ①增復合 | repeat reduced active segment and concat
    감 | continue while loop
  不 | else
    가 | stop while loop
  終 | end if
終 | end while

2
链接到解释器将很方便...另外,使用UTF-16还是86个字节?
彼得·泰勒

@PeterTaylor:谢谢,在文章中添加了指向解释器的链接。是的,在UTF-16中,确实有43个BMP字符转换为86个字节。
Timwi

5

k(83)

worm:{-1+*({x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y}.)/(1;|,/x)}

这可能可以进一步进行,因为它可以非常直接地实现重复。

基本的进化函数,{x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y}是65个字符,并使用一些技巧来阻止蠕虫死亡时增加年龄。包装器将单个整数的输入强制转换为列表,反转输入(以递归方式将蠕虫写成与您的记号相反的蠕虫要短一些),索取固定点,选择使用期限作为输出并调整结果以解决上一代产品中的过冲问题。

如果我手动执行强制和反向操作,则它会降至80({-1+*({x,,(,/((x+:i)#,@[y@&w;(i:~~#y)#0;-1+]),y@&~w:&\~y<*y;1_y)@~*y}.)/(1;x)})。

一些例子:

  worm 1 1 0
51
  worm 2
51
  worm 1 1
19

不幸的是,对于最大数量的可打印件,它可能用不了多少,除了从理论上讲,它,因为它相当慢,仅限于64位整数,并且可能不是特别节省内存。

特别是,worm 2 1worm 3刚刚翻腾(并可能会抛出'wsfull(内存不足),如果我让他们继续下去)。


我尝试使用此在线解释器运行您的程序,但未显示任何输出。(提交扩展名为.k的文本文件应该调用K解释器。)您知道将输出发送到stdout时会怎么做吗?
2014年

看起来它正在运行kona,它是k3的开源克隆。我的代码是用k4编写的,不太可能与k3兼容。您可以在kx.com/software-download.php上获得q / k4的限时免费副本;完成后,启动REPL,将` to switch from q`输入到k,然后粘贴我的代码。或者,您可以将我的代码保存在带有.k扩展名的文件中,并将其加载到解释器中。
亚伦·戴维斯

2

APL(Dyalog Unicode),52 字节SBCS

@ngn和@Adám节省了7个字节。

0{⍬≡⍵:⍺⋄n←⍺+10=⊃⍵:n1↓⍵⋄n∇∊(⊂1n/-∘1@1¨)@1⊆∘⍵⍳⍨⌊\⍵}⌽

在线尝试!

说明:

0{...}⌽     A monadic function train. We define a recursive function with two
            arguments: zero (our counter), and the reverse of our input
⍬≡⍵:⍺       Our base case - if our input is an empty list, return our counter
n←⍺+1       Define 'n' as our counter plus 1
0=⊃⍵:n1↓⍵  If the first element of the input is zero, recurse with the tail
            of our input and n
\⍵         Minimum-expand: creates a new list from our input where each element
            is the incremental minimum     
⍳⍨          Applies above to both sides of the index-of function. Index-of returns
            the index of the first occurence of each element in the left-side list.
            At this point, a (reversed) input list of [3 4 5 2 3 4] would result
            in [1 1 1 4 4 4]
⊆∘⍵         Partition, composed with our input. Partition creates sublists of the
            right input whenever the integer list in the left input increases.
            This means we now have a list of sub-lists, with the first element
            being the worm's active segment.
(...)@1    ⍝ Take the active segment and apply the following function train...
-∘1@1¨     ⍝ Subtract 1 from the first element of the active segment
1n/        ⍝ Replicate the resultant list above n+1 times
⊂          ⍝ Enclose the above, so as to keep the original shape of our sub-array
∊          ⍝ Enlist everything above together - this recursively concatenates our
           ⍝ new active segment with the remainder of the list
n∇         ⍝ Recurse with the above and n

我认为APL将为此提供一个非常干净的解决方案,这不是基于数组的语言吗?
ThePlasmaRailgun

1

斯卡拉198

type A=List[Int]
def T(w:A)={def s(i:Int,l:A):Stream[A]=l match{case f::r=>l#::s(i+1,if(f<1)r
else{val(h,t)=l.span(_>=l(0));List.fill(i)(h(0)-1::h.tail).flatten++t})
case _=>Stream()};s(2,w).length}

用法:

scala> T(List(2))
res0: Int = 51

1

K,95

{i::0;#{~x~,0}{((x@!b),,[;r]/[i+:1;r:{@[x;-1+#x;-1+]}@_(b:0^1+*|&h>x)_x];-1_x)@0=h:*|x:(),x}\x}

k)worm:{i::0;#{~x~,0}{((x@!b),,[;r]/[i+:1;r:{@[x;-1+#x;-1+]}@_(b:0^1+*|&h>x)_x];-1_x)@0=h:*|x:(),x}\x}
k)worm 2
51
k)worm 1 1
19
q)worm 1 1 0 0 0 0
635

1

C(gcc),396字节

#define K malloc(8)
typedef*n;E(n e,n o){n s=K,t=s;for(*s=*o;o=o[1];*t=*o)t=t[1]=K;t[1]=e;e=s;}main(c,f,l,j,a)n*f;{n w=K,x=w;for(;l=--c;x=x[1]=K)*x=atoi(f[c]);for(;w&&++l;)if(*w){n v=K,z=v,u=w,t=K;for(a=*v=*w;(u=u[1])&&*u>=*w;*z=*u)z=z[1]=K;for(x=v[1],v=K,*v=a-1,1[u=v]=x;u;u=u[1])w=w[1];for(j=~l;j++;)u=t=E(t,v);for(;(u=u[1])&&(x=u[1])&&x[1];);u[1]=0;w=w?E(w,t):t;}else w=w[1];printf("%d",--l);}

在线尝试!

我知道我晚会很晚,但是我想我会用C来尝试这一点,这需要一个链表实现。除了将所有标识符更改为单个字符外,它根本不是真正的高尔夫球手,但它确实起作用!

总而言之,考虑到这是我编写的第三个C / C ++程序,我非常高兴。


您真的需要一个链表吗?为什么不分配数组呢?由于这是代码高尔夫,因此您甚至不需要在完成后就释放它们。你可能甚至能够找到一种方法把它们存放到调用栈(不知道)上。
dfeuer

另外,您不需要主要功能。只需编写一个将蠕虫作为参数并返回其寿命的函数。蠕虫可以是一个数组及其长度,也可以是一个以负数结尾的数组。
dfeuer

1

Haskell,84个字节

(0!).reverse
n!(x:y)|x<1=(n+1)!y|(a,b)<-span(>=x)y=(n+1)!(([-1..n]*>x-1:a)++b)
n!_=n

在线尝试!

感谢@xnor提供了两个字节。

我觉得应该有一种排除常见增量的好方法,但是我还没有找到一个简短的增量。


1
两个小打高尔夫球:检查空单的情况下第二次和移位n1。降
同或

我也认为应该有一种方法不写(n+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.