Универсальный (изгибающий правила) Code Golf solver

14

Код гольф всегда включает в себя некоторые ответы, которые более или менее нарушают правила, нарушая ограничения, которые претенденты считали само собой разумеющимся или просто не думали и не указывали в правилах. Одна из этих интересных лазеек - это возможность выводить больше, чем требует задача, чтобы получить лучший результат.

Принимая это к крайности, мы можем написать универсальный код решатель гольфа, который печатает желаемый результат - если вам все равно, что это может занять много лет и выводит много других вещей до и после него.

Все, что нам нужно для вывода, это последовательность, которая гарантированно содержит все возможные подпоследовательности. Для этого кода гольф это будет последовательность Эренфойхта-Мичельского :

Последовательность начинается с трех битов 010; каждая последующая цифра формируется путем нахождения самого длинного суффикса последовательности, который также появляется ранее в последовательности, и дополнения бита после самого последнего более раннего появления этого суффикса.

Каждая конечная подпоследовательность битов происходит непрерывно, бесконечно часто внутри последовательности

Первые несколько цифр последовательности:

010011010111000100001111 ... (последовательность A038219 в OEIS ).

Объединяя 8 битов последовательности в байт, мы получим вывод ASCII, который мы можем вывести на экран или в файл и который содержит все возможные конечные выходные данные . Программа будет выводить части пи, текст «Никогда не отдам тебя» , немного хорошего ASCII-арта, собственный исходный код и все остальное, что вы можете захотеть вывести.

Для проверки правильности, вот хэши для первых 256 байтов последовательности:

MD5: 5dc589a06e5ca0cd9280a364a456d7a4
SHA-1: 657722ceef206ad22881ceba370d32c0960e267f

Первые 8 байтов последовательности в шестнадцатеричной записи:

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

Правила:

schnaader
источник
Самый длинный суффикс в 010, который появился ранее в этой последовательности, равен 0, не так ли? И самое последнее более раннее появление - это просто второе 0. И до сих пор ничто не следует за вторым 0, поэтому нет ничего, на чем мы можем построить дополнение. Я не носитель английского языка - возможно, я просто ошибся. Статья в Википедии использует те же слова, но имеет более длинную последовательность, поэтому я бы назвал ее «самая последняя ... у которой есть подписчик».
пользователь неизвестен
8
Педантичный спор: пи никогда не появится - только каждая конечная строка будет содержаться в выводе.
Кит Рэндалл
У меня другой вопрос: может ли повторение совпадать? Например, в 111, (1 [1) 1]?
пользователь неизвестен
@KeithRandall: Я бы предпочел, чтобы в последовательности, которая гарантированно не содержала «Никогда не отдам тебя», и аналогичные произведения.
пользователь неизвестен
2
Возможно, стоит упомянуть, что простое присутствие «ответа», встроенного в неопределенное место в бесконечной строке, конечно, нельзя рассматривать как «вывод» этого ответа. Кроме того, эта конкретная последовательность является лишь одним примером дизъюнктивной последовательности - таких последовательностей бесконечно много.
Res

Ответы:

7

C –110 символов

Эта версия программы использует алгоритм линейного времени выполнения для генерации последовательности. Вычитание 512 из 402 символов в программе дает в итоге 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];
    }
}

(Приведенный выше код, не основанный на гольфе, основан на коде, написанном Гжегожем Германом и Майклом Солтисом, как указано в описании проблемы, и на домашней странице Солтиса .)

Спасибо @schnaader и @res за сообщение об ошибке в начальной версии.

Хлебница
источник
Ницца! Это то, на что я надеялся с бонусом -512.
шнаадер
Любая идея, почему это вызывает сбои системы? Все версии для гольфа, без игры в гольф и mallocмодифицированные версии останавливают вывод после примерно 10000 байт и продолжают выделять память, что prog > out.datдает мгновенный сбой при использовании только ~ 700 КБ памяти. Если я вставлю printf("\n%i\n", size);после realloc, самый большой вывод 4. Система: Windows 7 Prof. 64-разрядная, 4 ГБ оперативной памяти, GCC 4.6.1
schnaader
(+1) Я считаю, что с Ubuntu12.04 / gcc обе ваши программы компилируются и выдают правильный вывод ... С Win7 / mingw / gcc обе программы компилируются, но выдают ошибки сегментации ... С Win7 / lcc версия без игры в гольф работает, но версия с гольфом производит ошибки сегментации.
Res
1
Это звучит как использование неинициализированных данных для меня. Конечно же, у меня нет доступа к машине с Windows, но проблема заключается в valgrind. Похоже, я воспроизвел эту ошибку и из исходной эталонной реализации. К счастью, это легко исправить; спасибо за сообщение об этом!
хлебница
Отлично, работает как шарм.
шнайдер
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позволит сохранить еще два символа.
Res
@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
Мой первоначальный сценарий Perl занял 10 секунд для 256 байтов. Эта версия занимает 90 секунд. (И при этом это не похоже на линейное замедление.)
хлебница