通用(弯曲)代码高尔夫求解器


14

高尔夫守则总是涉及一些挑战,这些挑战通过打破挑战者视为理所当然的约束,或者只是没有考虑并且没有在规则中列出来而或多或少地改变了规则。其中这些有趣的漏洞是可能输出比挑战询问,以获得更好的结果。

极端地讲,我们可以编写一个通用代码的高尔夫球求解器,打印出所需的输出-如果您不在乎它可能会变旧并在输出前后都输出很多其他内容。

我们需要输出的是一个序列,该序列保证包含每个可能的子序列。对于此代码高尔夫球,这将是Ehrenfeucht-Mycielski序列

该序列以三个位010开始;否则为0。通过找到也在序列中更早出现的序列的最长后缀,并对该后缀的最近较早出现之后的位进行补充,来形成每个连续的数字。

位的每个有限子序列都是连续出现的,在序列中通常是无限出现的

序列的前几个数字是:

010011010111000100001111 ...(OEIS中的序列A038219)。

将序列的8位组合为一个字节,我们将获得ASCII输出,可以将其输出到屏幕或文件,并包含所有可能的有限输出。该程序将输出pi的部分内容,“永不放弃”的歌词,一些漂亮的ASCII艺术作品,其自己的源代码以及您可能希望其输出的所有其他内容。

为了测试正确性,以下是序列的前256个字节的哈希值:

MD5: 5dc589a06e5ca0cd9280a364a456d7a4
SHA-1: 657722ceef206ad22881ceba370d32c0960e267f

序列的前8个字节以十六进制表示为:

4D 71 0F 65 27 46 0B 7C

规则:


在该序列中较早出现的010中最长的后缀是0,不是吗?最近的出现只是第二个0。直到现在,第二个0都没有出现,因此我们没有什么可以建立补码的。我不是说英语的人-也许我只是听错了。维基百科文章使用相同的词,但是序列更长,所以我将其命名为“最近的……具有关注者”。
用户未知

8
Pedantic quibble:永远不会出现pi- 输出中只会包含每个有限的字符串。
基思·兰德尔

我还有一个问题:重复是否可以重叠?例如在111中,(1 [1)1]?
用户未知

@KeithRandall:我希望能保证不会包含“永不放弃”和类似作品的序列。
用户未知

2
可能值得一提的是,在无限字符串中未指定位置嵌入的“答案”的存在当然不能视为“输出”该答案。同样,此特定序列只是分离序列的一个示例-无限多个这样的序列。
2012年

Answers:


7

C,–110个字符

该程序的该版本使用线性运行时算法来生成序列。从程序中的402个字符中减去512,得出的总数为负110。

#define C v=calloc(7,8),v->p=p
#define G(F,K)u->F[d[K]]
#define S(F,T)G(f,T)=F,G(t,T)=T,G(n,T)=
struct{int p,f[2],t[2];void*n[2];}r,*u,*v,*w;char*d,c;p,b,h,i,j,k;
main(s){for(;d=++p-s?d:realloc(d,s*=2);){d[i=p]=b;c+=c+b;p%8||putchar(c);
for(u=&r;b=u->p,u->p=p,w=G(n,k=i);S(i,k)v=G(n,k),u=v)for(h=G(f,k),j=G(t,k);j>h;--i,--j)
if(d[i]-d[j]){S(i,k)C;u=v;S(h,j)w;S(0,i)C;b=w->p;goto x;}S(0,i)C;x:b=1-d[b+1];}}

根据问题,程序在无限循环中运行,这需要大量内存分配,并且realloc()用于保持序列连续可以导致堆碎片。您可以通过calloc(7,8)在第一行中替换来提高程序的内存使用率calloc(1,sizeof*v)。这将在32位计算机上特别有用,其中56可能太大了两倍。

该代码有点不可读,而且不是一种有趣的方式。为此我深表歉意。坦白说,即使是非高尔夫版本也不是很清楚:

#include <stdio.h>
#include <stdlib.h>

typedef struct branch branch;
typedef struct node node;

struct branch {
    int from, to;
    node *next;
};

struct node {
    int pos;
    branch br[2];
};

static node root = { 0 };

static unsigned char *data = NULL;
static int endpos = 0;
static int size = 1;

static node *mknode(void)
{
    node *n;

    n = calloc(1, sizeof *n);
    n->pos = endpos;
    return n;
}

static branch *getbranch(node *n, int p)
{
    return &n->br[data[p]];
}

static void setbranch(node *n, int from, int to, node *next)
{
    n->br[data[to]].next = next;
    n->br[data[to]].from = from;
    n->br[data[to]].to = to;
}

int main(void)
{
    node *u, *v, *w;
    int follower, from, i, i0, j;
    int out, b;

    out = b = 0;
    for (;;) {
        ++endpos;
        if (endpos == size) {
            size *= 2;
            data = realloc(data, size);
        }
        data[endpos] = b;
        out = (out << 1) | b;
        if (endpos % 8 == 0) {
            putchar(out);
            out = 0;
        }

        i = endpos;
        u = &root;
        for (;;) {
            follower = u->pos + 1;
            u->pos = endpos;
            w = getbranch(u, i)->next;
            if (!w)
                break;
            i0 = i;
            from = getbranch(u, i0)->from;
            for (j = getbranch(u, i0)->to ; j > from ; --j) {
                if (data[i] != data[j]) {
                    /* divide branch */
                    v = mknode();
                    setbranch(u, i, i0, v);
                    u = v;
                    setbranch(u, from, j, w);
                    setbranch(u, 0, i, mknode());
                    follower = w->pos + 1;
                    goto bitfound;
                }
                --i;
            }
            v = getbranch(u, i0)->next;
            setbranch(u, i, i0, v);
            u = v;
        }
        /* extend branch */
        setbranch(u, 0, i, mknode());

      bitfound:
        b = 1 - data[follower];
    }
}

(上面的非代码化代码基于问题描述中以及Soltys主页上由Grzegorz Herman和Michael Soltys编写的代码。)

感谢@schnaader和@res报告了初始版本中的错误。


真好!这就是我希望获得的-512奖金。
schnaader 2012年

知道为什么这会导致系统崩溃吗?所有经过打高尔夫球,松开高尔夫球和malloc修改过的版本都会在大约10000个字节后停止输出,并继续分配内存,prog > out.dat仅使用约700 KB的内存即可立即崩溃。如果我插入printf("\n%i\n", size);realloc,最大的输出4。系统:Windows 7 Prof. 64位,4 GB RAM,GCC 4.6.1
schnaader 2012年

(+1)我发现使用Ubuntu12.04 / gcc,您的两个程序都可以编译并产生正确的输出...使用Win7 / mingw / gcc,这两个程序都可以编译,但会产生分段错误...使用Win7 / lcc,非高尔夫版本可以工作,但高尔夫版本会产生分段错误。
2012年

1
对我来说,这听起来像是使用未初始化的数据。果然-我没有Windows计算机的访问权限,但是valgrind显示了问题。看起来我也从原始参考实现中复制了此错误。幸运的是,这很容易解决。感谢您举报!
面包箱2012年

太好了,现在就像魅力一样。
schnaader 2012年

6

红宝石,109 104 101 94个字符

s=?0
loop{s=(s[/(.*).*\1/][/.#{$1}/]<?1??1:?0)+s
s.size&7<1&&$><<[s.reverse.to_i(2)].pack(?C)}

使用正则表达式进行后缀搜索的Ruby实现。由于要花很长时间才能耗尽内存,因此该程序必须由用户终止。

编辑:我刚刚注意到,从序列开始就足够了0

编辑2: res的提案节省了2个字符,另一些则是因为我们不必在之前删掉一个字节pack


使用s=(s[/(.*).*\1/][/.#{$1}/]<?1??1:?0)+s将保存另外两个字符。
2012年

@res这确实有效。谢谢。
霍华德

你能摆脱周围的括号?C吗?
基金莫妮卡的诉讼

4

Perl,95个字符

刚开始时,我实际上有一个不错的版本。然后当我打高尔夫球时,每个版本都变慢了。越来越慢。

$|=$_="010";
y///c%8||print pack"B*",/(.{8})$/while/(.+)$(?(?{m|.*$^N(.)|})(?{$_.=1-$^N})|(?!))/

$|=严格意义上讲,前三个字符()并不是必需的...但是,如果没有那个,您通常必须等待脚本完成生成完整的4096字节,然后才能看到任何输出。这将需要几个小时。也许几个世纪了;我不确定。我是否提到过该程序的性能随时间而变差?因此,因此,我感到不得不将它们包括在内。

另一方面,该脚本具有我创建过的最丑陋的正则表达式之一,因此我为它感到自豪。


1
不必担心性能,该算法无需优化即可达到O(N ^ 3)。我编写的简单Delphi程序花了大约30秒的256个字节,但是花了一个小时的1024个字节,所以我假设4096个字节要花一两天。当然,RegEx和空间优化有可能使情况变得更糟:)
schnaader 2012年

我最初的Perl脚本花了10秒占用了256个字节。此版本需要90秒。(也不是线性下降。)
面包店
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.