Pemecah kode Golf Universal (pelengkung aturan)

14

Code golf selalu melibatkan beberapa jawaban yang sedikit banyak membengkokkan aturan dengan melanggar batasan yang diterima oleh penantang atau hanya belum memikirkan dan tidak tercantum dalam aturan. Salah satu celah yang menarik ini adalah kemungkinan untuk menghasilkan lebih dari tantangan yang diminta untuk mendapatkan hasil yang lebih baik.

Mengambil ini ke ekstrem, kita dapat menulis pemecah kode golf universal yang mencetak output yang diinginkan - jika Anda tidak peduli itu mungkin butuh waktu lama dan menghasilkan banyak hal lain sebelum dan sesudahnya.

Yang kita butuhkan untuk output adalah urutan yang dijamin mengandung setiap kemungkinan berikutnya. Untuk golf kode ini, ini akan menjadi urutan Ehrenfeucht-Mycielski :

Urutan dimulai dengan tiga bit 010; setiap digit berurutan dibentuk dengan mencari sufiks terpanjang dari urutan yang juga muncul sebelumnya dalam urutan, dan melengkapi bit mengikuti penampilan sufiks sebelumnya yang paling baru.

Setiap akhir bit yang terbatas terjadi secara bersebelahan, tak terhingga sering dalam urutan

Beberapa digit pertama dari urutan tersebut adalah:

010011010111000100001111 ... (urutan A038219 dalam OEIS ).

Menggabungkan 8 bit dari urutan ke byte, kita akan mendapatkan output ASCII yang bisa kita output ke layar atau ke file dan yang berisi setiap kemungkinan output hingga . Program ini akan menampilkan bagian pi, lirik “Never will give up up” , beberapa seni ASCII yang bagus, kode sumbernya sendiri, dan semua yang Anda inginkan untuk ditampilkan.

Untuk menguji kebenaran, berikut adalah hash untuk 256 byte pertama dari urutan:

MD5: 5dc589a06e5ca0cd9280a364a456d7a4
SHA-1: 657722ceef206ad22881ceba370d32c0960e267f

8 byte pertama dari urutan dalam notasi heksadesimal adalah:

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

Aturan:

  • Program Anda harus menampilkan urutan Ehrenfeucht-Mycielski (tidak ada yang lain), menggabungkan 8 bit ke karakter byte / ASCII.

  • Program terpendek (jumlah karakter) menang. Kurangi 512 dari jumlah karakter Anda jika Anda berhasil membuat urutan dalam waktu linier per byte yang dihasilkan .

schnaader
sumber
Akhiran terpanjang di 010 yang muncul sebelumnya dalam urutan itu adalah 0, bukan? Dan penampilan sebelumnya yang paling baru adalah yang kedua. Dan sampai sekarang, tidak ada yang mengikuti 0 yang kedua, jadi tidak ada yang bisa kita bangun sebagai pelengkap. Saya bukan penutur asli bahasa Inggris - mungkin saya salah mengerti. Artikel wikipedia menggunakan kata-kata yang sama, tetapi memiliki urutan yang lebih panjang sehingga saya akan menamainya "yang terbaru ... yang memiliki pengikut".
pengguna tidak diketahui
8
Berdalih pedantic: pi tidak akan pernah muncul - hanya setiap string hingga akan terkandung dalam output.
Keith Randall
Saya punya pertanyaan lain: Bisakah pengulangan tumpang tindih? Misalnya di 111, (1 [1) 1]?
pengguna tidak diketahui
@KeithRandall: Saya lebih suka urutan yang dijamin tidak mengandung 'Never will give you up' dan produksi sejenis.
pengguna tidak diketahui
2
Mungkin perlu disebutkan bahwa kehadiran "jawaban" yang melekat pada lokasi yang tidak ditentukan dalam string yang tidak terbatas tidak dapat dianggap sebagai "mengeluarkan" jawaban itu, tentu saja. Juga, urutan khusus ini hanyalah salah satu contoh dari urutan disjungtif - ada banyak urutan seperti ini.
res

Jawaban:

7

C, –110 karakter

Versi program ini menggunakan algoritma linear-runtime untuk menghasilkan urutan. Mengurangi 512 dari 402 karakter dalam program memberikan total 110 negatif.

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

Sesuai masalahnya, program berjalan dalam loop tak terbatas, yang mengharuskan banyak alokasi memori, dan menggunakan realloc()untuk menjaga urutan yang berdekatan dapat berkontribusi untuk tumpukan fragmentasi. Anda dapat meningkatkan penggunaan memori program dengan mengganti calloc(7,8)pada baris pertama dengan calloc(1,sizeof*v). Ini akan membantu terutama pada mesin 32-bit, di mana 56 kemungkinan terlalu besar dengan faktor dua.

Kode semacam ini tidak dapat dibaca, dan tidak dengan cara yang menarik; untuk itu saya minta maaf. Terus terang, bahkan versi yang tidak diserang tidak terlalu jelas:

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

(Kode yang tidak diubah di atas berdasarkan pada kode yang ditulis oleh Grzegorz Herman dan Michael Soltys, sebagaimana dirujuk dalam deskripsi masalah, dan dari halaman utama Soltys .)

Terima kasih kepada @schnaader dan @res karena melaporkan bug dalam versi awal.

kotak roti
sumber
Bagus! Itulah yang saya harapkan dengan bonus -512.
schnaader
Adakah yang tahu mengapa ini menyebabkan crash oleh sistem? Semua mallocversi golf, ungolfed dan modifikasi menghentikan output setelah sekitar 10.000 byte dan terus mengalokasikan memori, prog > out.datmemberikan crash instan dengan hanya ~ 700 KB penggunaan memori. Jika saya menyisipkan printf("\n%i\n", size);setelah realloc, output terbesar adalah 4. Sistem: Windows 7 Prof. 64-Bit, 4 GB RAM, GCC 4.6.1
schnaader
(+1) Saya menemukan bahwa dengan Ubuntu12.04 / gcc, kedua program Anda mengkompilasi dan menghasilkan output yang benar ... Dengan Win7 / mingw / gcc, kedua program mengkompilasi tetapi menghasilkan kesalahan segmentasi ... Dengan Win7 / lcc, versi ungolfed berfungsi, tetapi versi golf menghasilkan kesalahan segmentasi.
res
1
Kedengarannya seperti penggunaan data yang tidak diinisialisasi untuk saya. Cukup yakin - Saya tidak memiliki akses ke mesin Windows, tetapi valgrind menunjukkan masalahnya. Sepertinya saya juga mereproduksi bug ini dari implementasi referensi asli. Untungnya ini adalah perbaikan yang mudah; terima kasih telah melaporkannya!
kotak roti
Hebat, bekerja seperti pesona sekarang.
schnaader
6

Ruby, 109 104 101 94 karakter

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

Implementasi di Ruby menggunakan ekspresi reguler untuk pencarian suffix. Karena ini membutuhkan waktu yang cukup lama hingga kehabisan memori, program harus diakhiri oleh pengguna.

Sunting: Saya baru memperhatikan bahwa itu sudah cukup untuk memulai dengan urutan 0.

Sunting 2: Proposal res menyimpan 2 karakter, beberapa lainnya karena kami tidak harus memotong satu byte sebelumnya pack.

Howard
sumber
Menggunakan s=(s[/(.*).*\1/][/.#{$1}/]<?1??1:?0)+sakan menyimpan dua karakter lain.
res
@res Ini memang bekerja. Terima kasih.
Howard
Bisakah Anda menyingkirkan tanda kurung di sekitar ?C?
Dana Gugatan Monica
4

Perl, 95 karakter

Saya sebenarnya memiliki versi setengah jalan yang layak pada awalnya. Kemudian saat saya bermain golf, setiap versi menjadi lebih lambat. Semakin lambat.

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

Tiga karakter pertama ( $|=) tidak perlu, secara tegas ... tetapi tanpa itu, Anda biasanya harus menunggu skrip untuk menyelesaikan menghasilkan 4096 byte penuh sebelum Anda akan melihat salah satu output. Dan itu akan memakan waktu berjam-jam. Mungkin berabad-abad; Saya tidak yakin. Apakah saya menyebutkan bahwa kinerja program ini agak memburuk dari waktu ke waktu? Jadi karena itu saya merasa harus memasukkan mereka ke dalam penghitungan.

Di sisi lain, skrip ini memiliki salah satu regeks terburuk yang pernah saya buat, jadi saya pikir saya bangga karenanya.

kotak roti
sumber
1
Jangan khawatir tentang kinerjanya, algoritmanya adalah O (N ^ 3) tanpa optimasi. Program Delphi sederhana yang saya tulis memerlukan waktu sekitar 30 detik untuk 256 byte, tetapi sekitar satu jam untuk 1024 byte, jadi saya anggap 4.096 byte membutuhkan satu atau beberapa hari. Tentu saja, RegEx dan optimasi ruang memiliki potensi untuk memperburuknya :)
schnaader
Skrip Perl awal saya membutuhkan waktu 10 detik untuk 256 byte. Versi ini membutuhkan waktu 90 detik. (
Kelihatannya