Bỏ phiếu đa số với Cellata Automata


31

Có một vấn đề thực sự quan trọng trong máy tự động di động được gọi là vấn đề Đa số :

Vấn đề đa số, hoặc nhiệm vụ phân loại mật độ là vấn đề tìm quy tắc tự động tế bào một chiều thực hiện chính xác việc bỏ phiếu đa số.

...

Với một cấu hình của máy tự động tế bào hai trạng thái với tổng số ô i + j, i trong số đó ở trạng thái 0 và j ở trạng thái một, một giải pháp đúng cho vấn đề bỏ phiếu cuối cùng phải đặt tất cả các ô thành 0 nếu i> j và cuối cùng phải đặt tất cả các ô thành một nếu i <j. Trạng thái cuối cùng mong muốn là không xác định nếu i = j.

Mặc dù đã được chứng minh rằng không có automata di động có thể giải quyết vấn đề đa số trong mọi trường hợp, có nhiều quy tắc có thể giải quyết nó trong phần lớn các trường hợp. Máy tự động Gacs-Kurdyumov-Levin có độ chính xác khoảng 78% với các điều kiện ban đầu ngẫu nhiên. Quy tắc GKL không phức tạp:

  • Bán kính 3, nghĩa là trạng thái mới của ô phụ thuộc vào 7 ô trước đó: chính nó, 3 ô bên phải và 3 ô bên trái.
  • Nếu một ô hiện tại O, trạng thái mới của nó là phần lớn của chính nó, ô bên trái và ô 3 bước về bên trái.
  • Nếu một ô hiện tại 1, trạng thái mới của nó là phần lớn của chính nó, ô bên phải và ô 3 bước bên phải.

Đây là một ví dụ:

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

Trong ví dụ này, máy tự động di động đã tính toán chính xác rằng 8> 6. Các ví dụ khác mất nhiều thời gian hơn và tạo ra một số mẫu thú vị trong thời gian đó. Dưới đây là hai ví dụ tôi tìm thấy ngẫu nhiên.

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

Đưa nó lên cấp độ tiếp theo

Theo như nghiên cứu trên internet của tôi đã chỉ ra, gần như tất cả các nghiên cứu học thuật về vấn đề đa số đã được thực hiện với các CA 2 bang. Trong thử thách này, chúng tôi sẽ mở rộng vấn đề đa số sang các CA 3 bang . Tôi sẽ gọi đây là vấn đề đa số . Đa số , hoặc đa số tương đối, đề cập đến điều kiện trong đó một trong các lựa chọn có nhiều phiếu hơn so với mỗi lựa chọn thay thế, nhưng không nhất thiết phải chiếm đa số trong tất cả các phiếu.

Báo cáo vấn đề

  1. Có một máy tự động di động 3 trạng thái với bán kính 3.
  2. 151 ô với điều kiện biên tròn.
  3. Các tế bào này được đưa ra một trạng thái bắt đầu ngẫu nhiên, với điều kiện duy nhất là 1 trong 3 trạng thái có số nhiều nghiêm ngặt. "Ngẫu nhiên" có nghĩa là phân phối thống nhất độc lập cho mỗi ô.
  4. Độ chính xác của quy tắc là tỷ lệ phần trăm của các điều kiện ban đầu ngẫu nhiên (hợp lệ) trong đó tất cả các ô được đồng bộ hóa với trạng thái chính xác (trạng thái có số nhiều) trong vòng 10000 thế hệ .
  5. Mục tiêu là tìm ra một quy tắc với độ chính xác cao,

Các trường hợp cạnh đa số: Bất kỳ cấu hình nào có 50/50/51 là cấu hình bắt đầu hợp lệ (vì có số nhiều nghiêm ngặt), trong khi bất kỳ cấu hình nào có 51/51/49 đều không hợp lệ (vì không có đa số nghiêm ngặt).

Không gian tìm kiếm là 3 ^ 3 ^ 7 (~ 3e1043), nằm ngoài tầm với của bất kỳ tìm kiếm toàn diện nào. Điều này có nghĩa là bạn sẽ cần sử dụng các kỹ thuật khác, như thuật toán di truyền, để giải quyết vấn đề này. Nó cũng sẽ mất một số kỹ thuật của con người.

Quy tắc thế hệ 10000 có thể thay đổi, tùy thuộc vào thời gian chạy / độ chính xác của quy tắc mọi người tìm thấy. Nếu nó quá thấp để cho phép tỷ lệ hội tụ hợp lý, thì tôi có thể nâng nó lên. Ngoài ra, tôi có thể hạ thấp nó để phục vụ như là một break-breaker.

Chiến thắng

Người chiến thắng là người nộp quy tắc CA bán kính 3 với độ chính xác cao nhất trong số tất cả các thí sinh.

Nội dung gửi của bạn nên bao gồm ...

  • Mô tả quy tắc (sử dụng mã Wolfram nếu cần)
  • Tỷ lệ chính xác và cỡ mẫu
  • Một lời giải thích có kích thước hợp lý về cách bạn phát hiện ra quy tắc, bao gồm các chương trình bạn đã viết để giải quyết nó hoặc bất kỳ kỹ thuật "thủ công" nào. (Đây là phần thú vị nhất, vì mọi thứ khác chỉ là số nguyên.)

Làm việc trước

  • Một bài báo của Juille và Pollack , mô tả cách họ phát triển quy tắc 2 trạng thái với độ chính xác 86%.
  • Bài viết này đã sử dụng r = 3, 149 tế bào, CA 2 trạng thái. Nó đã không cố gắng để giải quyết vấn đề đa số, tuy nhiên, nhưng thay vào đó để tìm quy tắc một cách nhanh chóng dẫn đến việc xen kẽ tất cả các- 1-all- 0mẫu. Mặc dù có những khác biệt này, tôi nghi ngờ nhiều kỹ thuật sẽ tương tự nhau.
  • Một (không hữu ích lắm vì nó nằm sau một tờ giấy trả tiền) của Wolz và de Oliviera hiện đang giữ kỷ lục 2 trạng thái

Tôi đã rất thất vọng / ngạc nhiên khi thấy điều này không liên quan gì đến việc bỏ phiếu nhiều .
mèo

2
@cat Tôi thực sự cảm thấy như nó. Mỗi bang của mỗi tế bào có thể đại diện cho "phiếu bầu" của mình (lựa chọn 1 trong 3 ứng cử viên) và mục tiêu là xác định người chiến thắng trong cuộc bầu cử.
PhiNotPi

2
Đây là một thử thách mã thú vị. Tôi không phải là người chơi gôn, vì vậy tôi luôn thấy vui khi thấy những câu đố như thế này.
Draco18

Câu trả lời:


6

Loại GKL cộng với leo đồi, 61,498%

  • Nếu một ô là 0, hãy nhìn vào các ô 3 ở bên trái, 1 ở bên trái và ở chính nó. Đặt giá trị thành đa số. Nếu đó là cà vạt, hãy giữ nguyên như vậy.

  • Nếu một ô là 1, hãy nhìn vào các ô 3 ở bên phải, 1 ở bên phải và ở chính nó. Đặt giá trị thành đa số. Nếu đó là cà vạt, hãy giữ nguyên như vậy.

  • Nếu một ô là 2, hãy nhìn vào các ô 2 ở bên trái, 2 ở bên phải và 3 ở bên phải. Đặt giá trị thành đa số. Nếu đó là cà vạt, hãy giữ nguyên như vậy.

Tôi đã nhận được 59453 ngay trên tổng số 100000, 59,453%

Một số đột biến và leo đồi dẫn đến 61498/100000 = 61,498%.

Có lẽ tôi sẽ kiểm tra thêm một chút và sẽ đăng thêm một số thông tin sau.


3
Bạn có thể nên bao gồm quy tắc 61,498% thực tế để mọi người có thể xác minh nó.
Martin Ender

Bạn có thể nên làm các bài kiểm tra bạn (sẽ) làm.
Erik the Outgolfer

5

"Chỉ cần ném 2 giây và làm GKL" - 55,7%

Thật không dễ để đoán được một quy tắc tốt sẽ là gì, vì vậy tôi đã cố gắng ít nhất đưa ra một cái gì đó sẽ đạt điểm trên 1/3. Chiến lược là cố gắng có được câu trả lời đúng khi trạng thái đa số là 0 hoặc 1 và chấp nhận thua lỗ nếu nó 2. Nó đạt 56,5% trên 100.000 thử nghiệm, bằng cách nào đó tốt hơn một chút so với dự kiến ​​sẽ nhân lên 78% ( điểm GKL) * 2/3 (phần nhỏ thời gian khi câu trả lời là 0 hoặc 1) = 52%.

Cụ thể hơn, chiến lược như sau:

  • Nếu ô là 0 hoặc 1, hãy lấy phần lớn của 3 ô như trong chiến lược GKL, nhưng bỏ qua bất kỳ hàng xóm nào là 2. Nếu đó là hòa, hãy để ô không thay đổi.
  • Nếu ô là 2, chọn bất kỳ số nào nhiều hơn 0 hoặc 1 trong toàn bộ vùng lân cận. Nếu đó là hòa, chọn giá trị ngoài cùng bên trái là 0 hoặc 1. Nếu tất cả các hàng xóm là 2, hãy ở lại 2.

Tôi đã sử dụng mã này để kiểm tra:

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

Điểm số cao hơn bạn mong đợi vì nó tăng theo giới hạn thế hệ. Điểm số 78% của GKL thực sự là cho một giới hạn rất nhỏ của vài trăm gens hoặc hơn. Ngược lại, 10.000 gens sẽ cho GKL tỷ lệ chính xác cao hơn, có thể phù hợp với kết quả bạn đang nhận được.
PhiNotPi

2

"Chỉ cần ăn cắp bất cứ điều gì tốt nhất và phát triển nó", bleh

Chỉnh sửa: ở trạng thái hiện tại, câu trả lời này, thay vì tìm các mẫu tốt hơn, sẽ tìm thấy một mẫu ngẫu nhiên tốt hơn.

Câu trả lời này mã hóa / giải mã các giải pháp bằng cách liệt kê tất cả các trạng thái dưới dạng số thứ ba (chữ số có nghĩa nhỏ nhất trước tiên). Giải pháp cho 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

Câu trả lời này đã được phát triển từ 55,7% của frageum, sử dụng mã sau đây. Mã này yêu cầu libop , là thư viện chỉ tiêu đề C ++ cá nhân của tôi. Rất dễ cài đặt, chỉ cần thực hiện git clone https://github.com/orlp/liboptrong cùng thư mục với nơi bạn đã lưu chương trình. Tôi đề nghị biên dịch với g++ -O2 -m64 -march=native -std=c++11 -g. Để phát triển nhanh, tôi cũng đề xuất tiền biên dịch libop bằng cách chạy lệnh trên 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;
}

0

Mã hóa bằng tay, 57,541%

Điều này thực sự chỉ nhìn vào 5 ô phía trên nó. Nó có thể được cải thiện bằng cách tăng phạm vi mà nó nhìn vào. Ran với 100.000 trường hợp thử nghiệm.

Thuật toán:

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

Gen:

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

Mã kiểm tra:

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())
}

Thí dụ

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.