Универсальный (изгибающий правила) 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

Правила:


Самый длинный суффикс в 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 секунд. (И при этом это не похоже на линейное замедление.)
хлебница
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.