Універсальний (вигин за правилами) Коль-гольвер


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
Педантичний каламбур: pi ніколи не з’явиться - лише кожен кінцевий рядок міститиметься у висновку.
Кіт Рендалл

У мене є ще одне питання: чи може повторення перекриватися? Наприклад у 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
шнадер

(+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.