Risolutore di golf di codice universale (che regola le regole)


14

Il code golf implica sempre alcune risposte che piegano più o meno le regole rompendo i vincoli che gli sfidanti hanno dato per scontato o semplicemente non ci hanno pensato e non hanno elencato nelle regole. Una di queste interessanti lacune è la possibilità di produrre più di quanto la sfida richieda per ottenere un risultato migliore.

Portando questo estremo, possiamo scrivere un risolutore di golf di codice universale che stampa l'output desiderato - se non ti interessa che potrebbe richiedere secoli e produrre molte altre cose prima e dopo.

Tutto ciò di cui abbiamo bisogno per produrre è una sequenza che garantisca contenere ogni possibile sottosequenza. Per questo codice golf, questa sarà la sequenza di Ehrenfeucht-Mycielski :

La sequenza inizia con i tre bit 010; ogni cifra successiva viene formata trovando il suffisso più lungo della sequenza che appare anche prima all'interno della sequenza e completando il bit che segue l'apparizione precedente più recente di quel suffisso.

Ogni sottosequenza finita di bit avviene in modo contiguo, infinitamente spesso all'interno della sequenza

Le prime cifre della sequenza sono:

010011010111000100001111 ... (sequenza A038219 in OEIS ).

Combinando 8 bit della sequenza in un byte, otterremo un output ASCII che possiamo produrre sullo schermo o su un file e che contiene ogni possibile output finito . Il programma produrrà parti di pi, i testi di "Never never give you up" , alcune belle opere d'arte ASCII, il suo codice sorgente e tutto il resto che si possa desiderare che venga pubblicato.

Per verificare la correttezza, ecco gli hash per i primi 256 byte della sequenza:

MD5: 5dc589a06e5ca0cd9280a364a456d7a4
SHA-1: 657722ceef206ad22881ceba370d32c0960e267f

I primi 8 byte della sequenza in notazione esadecimale sono:

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

Regole:

  • Il programma deve generare la sequenza Ehrenfeucht-Mycielski (nient'altro), combinando 8 bit con un carattere byte / ASCII.

  • Vince il programma più breve (conteggio caratteri). Sottrai 512 dal conteggio dei personaggi se riesci a generare la sequenza in tempo lineare per byte generato .


Il suffisso più lungo in 010 che è apparso prima in quella sequenza è 0, non è vero? E l'apparizione precedente più recente è solo il secondo 0. E fino ad ora, nulla segue il secondo 0, quindi non c'è nulla su cui possiamo costruire un complemento. Non sono un madrelingua inglese - forse ho sbagliato. L'articolo di Wikipedia usa le stesse parole, ma ha una sequenza più lunga, quindi lo chiamerei "il più recente ... che ha un follower".
utente sconosciuto

8
Cavillo pedante: il pi non apparirà mai - nell'output sarà contenuta solo ogni stringa finita .
Keith Randall,

Ho un'altra domanda: una ripetizione può sovrapporsi? Ad esempio in 111, (1 [1) 1]?
utente sconosciuto

@KeithRandall: Preferirei una sequenza che è garantita per non contenere "Non ti arrenderò mai" e produzioni di tipo simile.
utente sconosciuto

2
Vale la pena ricordare che la semplice presenza di una "risposta" incorporata in una posizione non specificata in una stringa infinita non può essere considerata come "emissione" di quella risposta, ovviamente. Inoltre, questa particolare sequenza è solo un esempio di una sequenza disgiuntiva : ci sono infinite sequenze come questa.
res

Risposte:


7

C, -110 caratteri

Questa versione del programma utilizza l'algoritmo di runtime lineare per generare la sequenza. Sottraendo 512 dai 402 caratteri nel programma si ottiene un totale di 110 negativi.

#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];}}

In base al problema, il programma viene eseguito in un ciclo infinito, il che richiede molta allocazione di memoria e l'utilizzo realloc()per mantenere la sequenza contigua può contribuire alla frammentazione dell'heap. È possibile migliorare l'utilizzo della memoria del programma sostituendolo calloc(7,8)con la prima riga calloc(1,sizeof*v). Ciò sarà particolarmente utile su una macchina a 32 bit, dove 56 è probabilmente troppo grande di un fattore due.

Il codice è in qualche modo illeggibile e non in modo interessante; per questo mi scuso. Francamente, anche la versione non golfata non è tremendamente chiara:

#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];
    }
}

(Il codice non golf sopra riportato si basa sul codice scritto da Grzegorz Herman e Michael Soltys, come indicato nella descrizione del problema, e dalla home page di Soltys .)

Grazie a @schnaader e @res per aver segnalato un bug nella versione iniziale.


Bello! Questo è quello che speravo con il bonus di -512.
Schnaader,

Qualche idea sul perché ciò causi arresti anomali da parte del sistema? Tutte le mallocversioni golfate, non golfate e modificate interrompono l'output dopo circa 10000 byte e continuano a allocare memoria, prog > out.datprovocando un arresto istantaneo con un utilizzo della memoria di soli ~ 700 KB. Se inserisco printf("\n%i\n", size);dopo realloc, l'output più grande è 4. Sistema: Windows 7 Prof. 64-Bit, 4 GB RAM, GCC 4.6.1
schnaader

(+1) Trovo che con Ubuntu12.04 / gcc, entrambi i programmi vengano compilati e producano l'output corretto ... Con Win7 / mingw / gcc, entrambi i programmi vengono compilati ma producono errori di segmentazione ... Con Win7 / lcc, il la versione ungolfed funziona, ma la versione golfed produce errori di segmentazione.
ris.

1
Mi sembra un uso di dati non inizializzati. Abbastanza sicuro: non ho accesso a un computer Windows, ma valgrind mostra il problema. Sembra che abbia riprodotto questo bug anche dall'implementazione di riferimento originale. Fortunatamente è una soluzione semplice; grazie per averlo segnalato!
breadbox

Fantastico, funziona come un fascino ora.
Schnaader,

6

Rubino, 109 104 101 94 caratteri

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

Implementazione in Ruby usando espressioni regolari per la ricerca dei suffissi. Dal momento che ci vuole molto tempo prima che la memoria sia esaurita, il programma deve essere chiuso dall'utente.

Modifica: ho appena notato che è sufficiente iniziare con la sequenza0 .

Modifica 2: la proposta di res salva 2 caratteri, alcuni altri perché non è necessario tagliare un singolo byte prima pack.


L'uso s=(s[/(.*).*\1/][/.#{$1}/]<?1??1:?0)+ssalverà altri due caratteri.
ris.

@res Funziona davvero. Grazie.
Howard l'

Riesci a sbarazzarti delle parentesi in giro ?C?
Fondi Monica's Lawsuit

4

Perl, 95 caratteri

All'inizio avevo una versione decente a metà. Poi, mentre giocavo a golf, ogni versione è diventata più lenta. Sempre più lento.

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

I primi tre caratteri ( $|=) non sono necessari, a rigor di termini ... ma senza quello lì, in genere dovresti aspettare che lo script finisca di generare un totale di 4096 byte prima di vedere qualsiasi output. E ciò richiederebbe ore. Forse secoli; Non ne sono sicuro. Ho già detto che le prestazioni di questo programma si deteriorano nel tempo? Quindi per questo motivo mi sono sentito in dovere di includerli nel conteggio.

D'altra parte, questa sceneggiatura ha una delle regex più brutte che io abbia mai creato, quindi penso di esserne orgoglioso.


1
Non preoccuparti delle prestazioni, l'algoritmo è O (N ^ 3) senza ottimizzazioni. Il mio semplice programma Delphi che ho scritto ha impiegato circa 30 secondi per 256 byte, ma circa un'ora per 1024 byte, quindi suppongo che 4096 byte impiegheranno uno o più giorni. Naturalmente, RegEx e le ottimizzazioni dello spazio hanno il potenziale per peggiorare le cose :)
schnaader

Il mio script Perl iniziale ha impiegato 10 secondi per 256 byte. Questa versione richiede 90 secondi. (Né sembra essere un rallentamento lineare.)
breadbox
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.