Voting Pluralitas dengan Cellular Automata

31

Ada masalah yang sangat penting dalam automata seluler yang disebut masalah Mayoritas :

Masalah mayoritas, atau tugas klasifikasi kerapatan adalah masalah menemukan aturan otomat seluler satu dimensi yang secara akurat melakukan pemungutan suara mayoritas.

...

Diberikan konfigurasi automata seluler dua-negara dengan total sel i + j, i di antaranya berada dalam keadaan nol dan j di antaranya berada dalam satu keadaan, solusi yang tepat untuk masalah pemilihan akhirnya harus mengatur semua sel menjadi nol jika i> j dan akhirnya harus mengatur semua sel menjadi satu jika saya <j. Status akhir yang diinginkan tidak ditentukan jika i = j.

Meskipun telah terbukti bahwa tidak ada automata seluler yang dapat memecahkan masalah mayoritas dalam semua kasus, ada banyak aturan yang dapat menyelesaikannya dalam sebagian besar kasus. Otomat Gacs-Kurdyumov-Levin memiliki akurasi sekitar 78% dengan kondisi awal acak. Aturan GKL tidak rumit:

  • Radius 3, artinya keadaan baru sel tergantung pada 7 sel sebelumnya: itu sendiri, 3 sel ke kanan, dan 3 sel ke kiri.
  • Jika sel saat ini O, keadaan barunya adalah mayoritas dari dirinya sendiri, sel di sebelah kirinya, dan sel 3 melangkah ke kiri.
  • Jika sebuah sel saat ini 1, keadaan barunya adalah mayoritas dari dirinya sendiri, sel di sebelah kanannya, dan sel 3 melangkah ke kanan.

Berikut ini sebuah contoh:

0 1 0 1 1 1 0 1 1 0 1 0 0 1
0 1 1 1 1 1 1 1 0 0 1 1 0 0
0 1 1 1 1 1 1 1 1 0 1 0 0 0
0 1 1 1 1 1 1 1 0 1 0 1 0 0
0 1 1 1 1 1 1 0 1 0 1 0 1 0
0 1 1 1 1 1 0 1 0 1 0 1 1 1
1 1 1 1 1 0 1 0 1 1 1 1 1 1
1 1 1 1 0 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1

Dalam contoh ini, otomat seluler menghitung dengan benar bahwa 8> 6. Contoh lain membutuhkan waktu yang lebih lama, dan menghasilkan beberapa pola yang keren untuk sementara waktu. Di bawah ini adalah dua contoh yang saya temukan secara acak.

0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 1 1 1 1 0 1 0 0 1 1 1
1 0 0 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 1 1

Membawanya ke tingkat berikutnya

Sejauh penelitian internet saya telah menunjukkan, hampir semua penelitian akademik tentang masalah mayoritas telah dilakukan dengan 2-negara CA. Dalam tantangan ini, kita akan memperluas masalah mayoritas menjadi CA 3-negara . Saya akan menyebutnya masalah pluralitas . Pluralitas , atau mayoritas relatif, merujuk pada kondisi di mana salah satu opsi memiliki lebih banyak suara daripada masing-masing alternatif, tetapi tidak harus mayoritas dari semua suara.

Pernyataan masalah

  1. Ada 3-state 1D seluler otomat dengan jari-jari 3.
  2. Ada 151 sel dengan kondisi batas melingkar.
  3. Sel-sel ini diberi keadaan awal yang acak, dengan syarat bahwa 1 dari 3 keadaan memiliki pluralitas yang ketat. "Acak" berarti distribusi seragam yang independen untuk setiap sel.
  4. Keakuratan aturan adalah persentase dari kondisi awal acak (valid) di mana semua sel disinkronkan ke keadaan yang benar (satu dengan pluralitas) dalam 10.000 generasi .
  5. Tujuannya adalah untuk menemukan aturan dengan akurasi tinggi,

Kasus tepi pluralitas: Konfigurasi apa pun dengan 50/50/51 adalah konfigurasi awal yang valid (karena ada pluralitas yang ketat), sedangkan konfigurasi dengan 51/51/49 tidak valid (karena tidak ada pluralitas yang ketat).

Ruang pencarian adalah 3 ^ 3 ^ 7 (~ 3e1043), jauh di luar jangkauan pencarian lengkap. Ini berarti Anda harus menggunakan teknik lain, seperti algoritma genetika, untuk menyelesaikan masalah ini. Ini juga akan membutuhkan rekayasa manusia.

Aturan generasi 10.000 dapat berubah, tergantung pada runtimes / keakuratan aturan yang ditemukan orang. Jika terlalu rendah untuk memungkinkan tingkat konvergensi yang masuk akal, maka saya bisa menaikkannya. Atau, saya bisa menurunkannya untuk berfungsi sebagai tie-breaker.

Kemenangan

Pemenangnya adalah orang yang menyerahkan aturan radius-3 CA dengan akurasi tertinggi dari semua kontestan.

Kiriman Anda harus mencakup ...

  • Deskripsi aturan (menggunakan kode Wolfram jika perlu)
  • Tingkat akurasi dan ukuran sampel
  • Sebuah berukuran cukup penjelasan tentang bagaimana Anda menemukan aturan, termasuk program-program yang Anda tulis untuk menyelesaikannya, atau "manual" rekayasa. (Ini menjadi bagian yang paling menarik, karena yang lainnya hanyalah angka mentah.)

Pekerjaan Sebelumnya

  • Sebuah makalah oleh Juille dan Pollack , menggambarkan bagaimana mereka mengembangkan aturan 2-negara dengan akurasi 86%.
  • Makalah ini menggunakan r = 3, 149-sel, 2-CA CAs. Itu tidak berusaha untuk memecahkan masalah mayoritas, namun, tetapi untuk menemukan aturan yang dengan cepat menghasilkan pola semua-bolak- 1balik 0. Terlepas dari perbedaan ini, saya menduga banyak teknik akan serupa.
  • Kertas (tidak terlalu membantu karena ada di balik paywall) karya Wolz dan de Oliviera yang saat ini memegang rekor 2-negara bagian
PhiNotPi
sumber
Saya sangat kecewa / terkejut menemukan ini tidak ada hubungannya dengan Voting Pluralitas .
kucing
2
@ kucing saya benar-benar merasa seperti itu. Setiap negara sel dapat mewakili "suara" (pilihan 1 dari 3 kandidat), dan tujuannya adalah untuk menentukan pemenang pemilihan.
PhiNotPi
2
Ini adalah tantangan kode yang menarik. Saya bukan seorang pegolf, jadi selalu senang melihat teka-teki semacam ini.
Draco18s

Jawaban:

6

Jenis GKL plus pendakian bukit, 61.498%

  • Jika sel adalah 0, lihat sel 3 di sebelah kiri, 1 di sebelah kiri dan di sel itu sendiri. Tetapkan nilai ke mayoritas. Jika ini seri, tetaplah seperti sekarang.

  • Jika sebuah sel adalah 1, lihat sel 3 di sebelah kanan, 1 di sebelah kanan dan dengan sendirinya. Tetapkan nilai ke mayoritas. Jika ini seri, tetaplah seperti sekarang.

  • Jika sebuah sel adalah 2, lihat sel 2 di sebelah kiri, 2 di sebelah kanan dan 3 di sebelah kanan. Tetapkan nilai ke mayoritas. Jika ini seri, tetaplah seperti sekarang.

Saya mendapatkan 59453 dari total 100000, 59,453%

Beberapa mutasi dan mendaki bukit menghasilkan 61498/100000 = 61,498%.

Saya mungkin akan menguji lebih sedikit dan akan memposting beberapa informasi lebih lanjut nanti.

Michael Stocker
sumber
3
Anda mungkin harus memasukkan aturan 61.498% aktual sehingga orang dapat memverifikasinya.
Martin Ender
Anda mungkin harus melakukan tes yang akan Anda lakukan.
Erik the Outgolfer
5

"Hanya membuang 2s dan lakukan GKL" - 55,7%

Tidak mudah menebak apa aturan yang baik, jadi saya mencoba untuk setidaknya menghasilkan sesuatu yang mendapat skor di atas 1/3. Strateginya adalah mencoba untuk mendapatkan jawaban yang benar ketika negara bagian mayoritas adalah 0 atau 1, dan menerima kerugian jika 2. Ini mencetak 56,5% dari 100.000 percobaan, yang entah bagaimana sedikit lebih baik daripada apa yang diharapkan dari mengalikan 78% ( skor GKL) * 2/3 (fraksi waktu ketika jawaban adalah 0 atau 1) = 52%.

Lebih konkretnya, strateginya adalah sebagai berikut:

  • Jika sel adalah 0 atau 1, ambil sebagian besar dari 3 sel seperti dalam strategi GKL, tetapi abaikan tetangga yang 2. Jika itu seri, biarkan sel tidak berubah.
  • Jika selnya 2, pilih mana yang lebih banyak dari 0 atau 1 di seluruh lingkungan. Jika ini seri, pilih nilai paling kiri yaitu 0 atau 1. Jika semua tetangga adalah 2, tetap 2.

Saya menggunakan kode ini untuk menguji:

#include <iostream>
#include <algorithm>
#include <string.h>
#include <random>
#include <cassert>

#define W 151
#define S 3
#define R 3

typedef int state;

struct tape {
    state s[R+W+R];
    state& operator[](int i) {
        return s[i + R];
    }
    template<typename Rule> void step(Rule r) {
        for(int i = 0; i < R; i++) s[i] = s[W + i];
        for(int i = 0; i < R; i++) s[R + W + i] = s[R + i];
        for(int i = 0; i < W; i++) {
            s[i] = r(s + R + i);
        }
        memmove(s + R, s, W * sizeof(*s));
    }

    state unanimous() {
        state st = (*this)[0];
        for(int i = 1; i < W; i++) {
            if((*this)[i] != st)
                return -1;
        }
        return st;
    }
};

std::ostream& operator<<(std::ostream& s, tape& t) {
    for (int i = 0; i < W; i++)
        s << t[i];
    return s;
}

state randomize(tape& t) {
    static std::mt19937 rg(390332);
    begin:
    int c[S]{};
    for(int i = 0; i < W; i++) {
        state s = rg() % S;
        c[s]++;
        t[i] = s;
    }
    state* smax = std::max_element(c, c + R);
    int nmaj = 0;
    for (int n : c) nmaj += n == *smax;
    if (nmaj > 1) goto begin;
    return smax - c;
}

template<bool PrintSteps, typename Rule> int simulate(Rule r, int trials, int giveup) {
    int successes = 0;
    for(state s = 0; s < S; s++) {
        state t[2 * R + 1];
        for(int i = 0; i <= 2 * R; i++) t[i] = s;
        assert(r(t + R) == s);
    }
    while(trials--) {
        tape tp;
        state desired = randomize(tp);
        int steps = giveup;
        while(steps--) {
            tp.step(r);
            state u = tp.unanimous();
            if(~u) {
                bool correct = u == desired;
                if(PrintSteps) {
                    std::cout << correct << ' ' << giveup - steps << '\n';
                }
                successes += correct;
                break;
            }
        }
    }
    return successes;
}


struct ixList {
    int n;
    int i[2 * R + 1];
};



state rule_justTossOutThe2sAndDoGKL(const state* t) {
    const ixList ixl[] = {
        { 3,{ -3, -1, 0 } },
        { 3,{ 0, 1, 3 } },
        { 6,{ -3, -2, -1, 1, 2, 3 } } 
    };
    int c[S]{};
    for (int i = 0; i < ixl[*t].n; i++)
        c[t[ixl[*t].i[i]]]++;
    if (c[0] > c[1]) return 0;
    if (c[1] > c[0]) return 1;
    if (*t < 2) return *t;
    for (int i = -R; i <= R; i++)
        if (t[i] < 2) return t[i];
    return 2;
}

int main()
{
    int nt = 100000;
    int ns = simulate<false>(rule_justTossOutThe2sAndDoGKL, nt, 10000);

    std::cout << (double)ns / nt << '\n';
    return 0;
}
feersum
sumber
Skor lebih tinggi dari yang Anda harapkan karena meningkat dengan batas generasi. Skor 78% GKL sebenarnya untuk batas yang sangat kecil yaitu sekitar beberapa ratus gens. Sebaliknya, 10.000 gens akan memberikan GKL tingkat akurasi yang lebih tinggi, mungkin sejalan dengan hasil yang Anda dapatkan.
PhiNotPi
2

"Curi apa saja yang terbaik dan kembangkanlah", bleh

Sunting: dalam keadaan saat ini, jawaban ini, alih-alih menemukan pola yang lebih baik, menemukan sampel acak yang lebih baik.

Jawaban ini menyandikan / mendekodekan solusi dengan menghitung semua status sebagai angka ternary (digit paling signifikan pertama). Solusi untuk 59,2%:

000000000010010010000000000000000000000000000000000000000000010000010000110000000
000000000010010010000000000111111101111111111111111111000011000010011011000011010
000000000012010011001000000021111111111120111211111111000000000000011010000010000
000011000010022110000000202000000002000000000020000000001010000000011011000011010
020000000010010010001000000111101111111111111111111111010011000011111111010011010
000000000010010010000000000111111111101111111111112111000011010110111011010011011
000000000010010010000000000010000000000000000100002011000000000100011010020010000
000020020010010010000200000111102111111111111111111101000011010010111011000011011
000100000010010010000000000121111111111111111111111111000210000012011011002011010
000000000010010110000000000111112112111111111001111111000010000010011011000011010
000000000010010120000200000111211111111111111111110111110011010011100111010011011
000000000010010010000000000011111111111111111111111111000011010010111211210012020
010000000010010010020100020111110111111111111111111110010111010011011111010111011
002000000010010010000000000111110111111111211111111111001111111111111111111111111
000000000110010010000000000111111111111111211111111111010111011111111111011111011
001000000010010010000000000011111101111111111111110111000011010010111011010011010
001000000010010110000000000111111111111111102111110111010111011111111111011111101
000000000210010010000000000111111111111111111111011111010011010011111111010111011
000000000010010010000000000112111111111111111111101011000000000000011010000010000
000000000010010010000000000111111111111111111111111111000011010010111011010011011
000200000012010010000000000111111111111112111111111111000010000210011211001011010
000000000010010211000002000111111111111111111111111111000001010010111011010011010
000021200010210010000101100111111111111211111110110211010111021111111101010111111
000000000010010010000000000111111111111101111111111111010011010111111111010110021
000200000010010010000000010111111111101111111121112111000210001010011011000011010
000000000010010010000000000111111111111111111111111111210011010021111111010111011
000020000010010010000000000111111111111111111111111111000011010010121011010011012

Jawaban ini berevolusi dari 55,7% feersum, menggunakan kode berikut. Kode ini memerlukan libop , yang merupakan perpustakaan khusus header C ++ saya. Ini sangat mudah dipasang, cukup lakukan git clone https://github.com/orlp/libopdi direktori yang sama dengan tempat Anda menyimpan program. Saya sarankan kompilasi dengan g++ -O2 -m64 -march=native -std=c++11 -g. Untuk pengembangan cepat saya juga menyarankan libop prakompilasi dengan menjalankan perintah di atas libop/op.h.

#include <cstdint>
#include <algorithm>
#include <iostream>
#include <cassert>
#include <random>

#include "libop/op.h"

constexpr int MAX_GENERATIONS = 500;
constexpr int NUM_CELLS = 151;

std::mt19937_64 rng;

double worst_best_fitness;

// We use a system with okay-ish memory density. We represent the ternary as a
// 2-bit integer. This means we have 32 ternaries in a uint64_t.
//
// There are 3^7 possible states, requiring 4374 bits. We store this using 69
// uint64_ts, or little over half a kilobyte.

// Turn 7 cells into a state index, by encoding as ternary.
int state_index(const int* cells) {
    int idx = 0;
    for (int i = 0; i < 7; ++i) {
        idx *= 3;
        idx += cells[6-i];
    }
    return idx;
}

// Get/set a ternary by index from a 2-bit-per-ternary encoded uint64_t array.
int get_ternary(const uint64_t* a, size_t idx) {
    return (a[idx/32] >> (2*(idx % 32))) & 0x3;
}

void set_ternary(uint64_t* a, size_t idx, int val) {
    assert(val < 3);
    int shift = 2*(idx % 32);
    uint64_t shifted_val = uint64_t(val) << shift;
    uint64_t shifted_mask = ~(uint64_t(0x3) << shift);
    a[idx/32] = (a[idx/32] & shifted_mask) | shifted_val;
}


struct Rule {
    uint64_t data[69];
    double cached_fitness;

    Rule(const char* init) {
        cached_fitness = -1;
        for (auto i : op::range(69)) data[i] = 0;
        for (auto i : op::range(2187)) set_ternary(data, i, init[i] - '0');
    }

    double fitness(int num_tests = 1000);

    Rule* random_mutation(int num_mutate) const {
        auto new_rule = new Rule(*this);

        auto r = op::range(2187);
        std::vector<int> indices;
        op::random_sample(r.begin(), r.end(),
                          std::back_inserter(indices), num_mutate, rng);

        for (auto idx : indices) {
            set_ternary(new_rule->data, idx, op::randint(0, 2, rng));
        }

        new_rule->cached_fitness = -1;
        return new_rule;
    }

    int new_state(const int* cells) const {
        return get_ternary(data, state_index(cells));
    }

    void print_rule() const {
        for (auto i : op::range(2187)) {
            std::cout << get_ternary(data, i);
            if (i % 81 == 80) std::cout << "\n";
        }
    }
};


struct Automaton {
    uint64_t state[5];
    int plurality, generation;

    Automaton() : generation(0) {
        for (auto i : op::range(5)) state[i] = 0;

        int strict = 0;
        while (strict != 1) {
            int votes[3] = {};
            for (auto i : op::range(NUM_CELLS)) {
                int vote = op::randint(0, 2, rng);
                set_ternary(state, i, vote);
                votes[vote]++;
            }

            // Ensure strict plurality.
            plurality = std::max_element(votes, votes + 3) - votes;
            strict = 0;
            for (auto i : op::range(3)) strict += (votes[i] == votes[plurality]);
        }
    }

    void print_state() {
        for (int i = 0; i < 151; ++i) std::cout << get_ternary(state, i);
        std::cout << "\n";
    }

    bool concensus_reached() {
        int target = get_ternary(state, 0);
        for (auto i : op::range(NUM_CELLS)) {
            if (get_ternary(state, i) != target) return false;
        }

        return true;
    }

    void next_state(const Rule& rule) {
        uint64_t new_state[5] = {};

        std::vector<int> cells;
        for (auto r : op::range(-3, 4)) {
            cells.push_back(get_ternary(state, (r + NUM_CELLS) % NUM_CELLS));
        }

        for (auto i : op::range(NUM_CELLS)) {
            set_ternary(new_state, i, rule.new_state(cells.data()));
            cells.erase(cells.begin());
            cells.push_back(get_ternary(state, (i + 4) % NUM_CELLS));
        }

        for (auto i : op::range(5)) state[i] = new_state[i];
        generation++;
    }
};


double Rule::fitness(int num_tests) {
    if (cached_fitness == -1) {
        cached_fitness = 0;
        int num_two = 0;
        for (auto test : op::range(num_tests)) {
            Automaton a;
            while (a.generation < MAX_GENERATIONS && !a.concensus_reached()) {
                a.next_state(*this);
            }

            if (a.generation < MAX_GENERATIONS &&
                get_ternary(a.state, 0) == a.plurality &&
                a.plurality == 2) num_two++;

            cached_fitness += (a.generation < MAX_GENERATIONS &&
                               get_ternary(a.state, 0) == a.plurality);

            if (cached_fitness + (num_tests - test) < worst_best_fitness) break;
        }

        if (num_two) std::cout << cached_fitness << " " << num_two << "\n";

        cached_fitness;
    }

    return cached_fitness;
}



int main(int argc, char** argv) {
    std::random_device rd;
    rng.seed(42); // Seed with rd for non-determinism.

    const char* base = 
        "000000000010010010000000000000000000000000000000000000000000000000010000000000000"
        "000000000010010010000000000111111111111111111111111111000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111000000000000011010000010000"
        "000000000010010010000000000000000000000000000000000000000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011011"
        "000000000010010010000000000000000000000000000000000000000000000000011010000010000"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011011"
        "000000000010010010000000000111111111111111111111111111000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011010"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111011111111111111111111111111"
        "000000000010010010000000000111111111111111111111111111010111011111111111011111111"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011010"
        "000000000010010010000000000111111111111111111111111111010111011111111111011111111"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111000000000000011010000010000"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011011"
        "000000000010010010000000000111111111111111111111111111000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011010"
        "000000000010010010000000000111111111111111111111111111010111011111111111011111111"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011012"
    ;

    // Simple best-only.
    std::vector<std::unique_ptr<Rule>> best_rules;
    best_rules.emplace_back(new Rule(base));
    worst_best_fitness = best_rules.back()->fitness();
    while (true) {
        const auto& base = *op::random_choice(best_rules.begin(), best_rules.end(), rng);
        std::unique_ptr<Rule> contender(base->random_mutation(op::randint(0, 100, rng)));

        // Sort most fit ones to the beginning.
        auto most_fit = [](const std::unique_ptr<Rule>& a, const std::unique_ptr<Rule>& b) {
            return a->fitness() > b->fitness();
        };

        if (contender->fitness() >= best_rules.back()->fitness()) {
            std::cout << contender->fitness();
            double contender_fitness = contender->fitness();
            best_rules.emplace_back(std::move(contender));
            std::sort(best_rules.begin(), best_rules.end(), most_fit);
            if (best_rules.size() > 5) best_rules.pop_back();
            std::cout << " / " << best_rules[0]->fitness() << "\n";
            worst_best_fitness = best_rules.back()->fitness();

            if (contender_fitness == best_rules.front()->fitness()) {
                best_rules.front()->print_rule();
            }
        }
    }

    return 0;
}
orlp
sumber
0

Kode Tangan, 57.541%

Ini sebenarnya hanya terlihat pada 5 sel di atasnya. Mungkin bisa ditingkatkan dengan meningkatkan rentang yang terlihat. Berlari dengan 100.000 test case.

Algoritma:

If above == 0:
   if to the left there are only 2s or there is a 1 separated by 2s
       next state = 2
   else
       next state = 0
If above == 1:
   if to the right there are only 2s or there is a 0 separated by 2s
       next state = 2
   else
       next state = 1
If above == 2:
   ignore 0s to the left if the 0 is left of a 1 on the left
   ignore 1s to the right if the 1 is right of a 0 on the right
   if the closest 0 on the left is closer than the closest 1 on the right
       next state = 0
   else if the closest 1 on the right is closer than the closest 0 on the left
       next state = 1
   else
       next state = 2

Gene:

000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222222222222222222222222222222000000000222222222222222222
000000000222222222000222222111111111111111111111111111222111111111111111111111111
000000000222222222000222222111111111111111111111111111000000000111111111222111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222222222222222222222222222222000000000222222222222222222
000000000222222222000222222111111111111111111111111111222111111111111111111111111
000000000222222222000222222111111111111111111111111111000000000111111111222111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222222222222222222222222222222000000000222222222222222222
000000000222222222000222222111111111111111111111111111222111111111111111111111111
000000000222222222000222222111111111111111111111111111000000000111111111222111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222

Kode pengujian:

import java.lang.Math.*
import java.util.*

const val RADIUS = 3;
const val STATES = 3;
const val DIAMETER = 2 * RADIUS + 1
const val TAPE_LENGTH = 151

val CODE_SIZE = pow(STATES.toDouble(), DIAMETER.toDouble()).toInt()

const val GRADE_RUNS = 100000
const val GRADE_MAX_TIME = 10000


operator fun IntArray.inc() : IntArray {
    val next = this.clone()
    var i = 0
    while (i < size) {
        if (this[i] == STATES - 1) {
            next[i] = 0
        } else {
            next[i]++
            break
        }
        i++
    }
    return next
}
val IntArray.index : Int
    get() {
        var total = 0
        for (i in (size - 1) downTo 0) {
            total *= STATES
            total += this[i]
        }
        return total
    }

interface IRule {
    operator fun get(states : IntArray) : Int
}

fun IntArray.equalsArray(other: IntArray) = Arrays.equals(this, other)

class Rule : IRule {

    constructor(rule : IRule) {
        val start = IntArray(DIAMETER)
        var current = start.clone()

        code = IntArray(CODE_SIZE)
        try {
            do {
                code[current.index] = rule[current]
                current++
            } while (!current.equalsArray(start));
        } catch (e : Throwable) {
            println(Arrays.toString(code))
            println(Arrays.toString(current))
            throw e
        }
    }
    constructor(code : IntArray) {
        this.code = IntArray(CODE_SIZE) { if (it < code.size) code[it] else 0 }
    }

    val code : IntArray

    override fun get(states: IntArray) : Int {
        return code[states.index]
    }

    override fun toString() : String {
        val b = StringBuilder()
        for (i in 0 until CODE_SIZE) {
            if (i > 0 && i % pow(STATES.toDouble(), RADIUS.toDouble() + 1).toInt() == 0) {
                b.append('\n')
            }
            b.append(code[i])
        }
        return b.toString()
    }

    fun grade() : Double {
        var succeeded = 0
        for (i in 0 until GRADE_RUNS) {
            if (i % (GRADE_RUNS / 100) == 0) {
                println("${i/(GRADE_RUNS / 100)}% done grading.")
            }
            var tape : Tape
            do {
                tape = Tape()
            } while (tape.majority() == -1);
            val majority = tape.majority()
            val beginning = tape
            var j = 0
            while (j < GRADE_MAX_TIME && !tape.allTheSame()) {
                tape = tape.step(this)
                j++
            }
            if (tape.stabilized(this) && tape.majority() == majority) {
                succeeded++
            }/* else if (beginning.majority() != 2) {
                println(beginning.majority())
                tape = beginning
                for (j in 1..100) {
                    println(tape)
                    tape = tape.step(this)
                }
                println(tape)
            }*/
        }
        return succeeded.toDouble() / GRADE_RUNS
    }

}

fun getRandomState() : Int {
    return (random() * STATES).toInt()
}

class Tape(val tape : IntArray) {

    constructor() : this(IntArray(TAPE_LENGTH) { getRandomState() } )

    fun majority() : Int {
        val totals = IntArray(STATES)

        for (cell in tape) {
            totals[cell]++
        }

        var best = -1
        var bestScore = -1

        for (i in 0 until STATES) {
            if (totals[i] > bestScore) {
                best = i
                bestScore = totals[i]
            } else if (totals[i] == bestScore) {
                best = -1
            }
        }

        return best
    }

    fun allTheSame() : Boolean {
        for (i in 1 until TAPE_LENGTH) {
            if (this[i] != this[0]) {
                return false
            }
        }
        return true
    }

    operator fun get(index: Int) = tape[((index % TAPE_LENGTH) + TAPE_LENGTH) % TAPE_LENGTH]

    fun step(rule : IRule) : Tape {
        val nextTape = IntArray ( TAPE_LENGTH )

        for (i in 0 until TAPE_LENGTH) {
            nextTape[i] = rule[IntArray(DIAMETER) { this[i + it - RADIUS] }]
        }

        return Tape(nextTape)
    }

    fun stabilized(rule : IRule) = allTheSame() && majority() == step(rule).majority()

    override fun toString() : String {
        val b = StringBuilder()
        for (cell in tape) {
            b.append(cell)
        }
        return b.toString()
    }

}

fun main(args : Array<String>) {
    val myRule = Rule(object : IRule {
        override fun get(states: IntArray): Int {
            if (states[3] == 0) {
                if (states[2] == 1) {
                    return 2
                } else if (states[2] == 0) {
                    return 0
                } else if (states[1] == 1) {
                    return 2
                } else if (states[1] == 0) {
                    return 0
                } else {
                    return 2
                }
            } else if (states[3] == 1) {
                if (states[4] == 0) {
                    return 2
                } else if (states[4] == 1) {
                    return 1
                } else if (states[5] == 0) {
                    return 2
                } else if (states[5] == 1) {
                    return 1
                } else {
                    return 2
                }
            } else {
                if (states[2] == 0) {
                    if (states[4] != 1) {
                        return 0
                    }
                } else if (states[4] == 1) {
                    return 1
                }
                if (states[1] == 0 && states[2] != 1) {
                    if (states[5] != 1) {
                        return 0
                    }
                } else if (states[5] == 1 && states[4] != 0) {
                    return 1
                }
                return 2
            }
        }

    })
    var tape = Tape()
    println(myRule.grade())
}

Contoh

TheNumberOne
sumber