셀룰러 오토마타를 이용한 복수 투표


31

셀룰러 오토마타에는 대다수 문제 라는 중요한 문제가 있습니다 .

대다수 문제 또는 밀도 분류 작업은 다수 투표를 정확하게 수행하는 1 차원 셀룰러 오토 마톤 규칙을 찾는 문제입니다.

...

i + j 셀 합계를 가진 2 상태 셀룰러 오토마타의 구성, i가 0 상태에 있고 j가 1 상태에있는 경우, 투표 문제에 대한 올바른 솔루션은 결국 모든 셀을 0으로 설정해야합니다. i> j이며 i <j 인 경우 결국 모든 셀을 1로 설정해야합니다. i = j 인 경우 원하는 최종 상태는 지정되지 않습니다.

셀룰러 오토마타가 모든 경우에서 대부분의 문제를 해결할 수 없다는 것이 입증되었지만 대부분의 경우이를 해결할 수있는 규칙이 많이 있습니다. Gacs-Kurdyumov-Levin 오토 마톤은 무작위 초기 조건에서 정확도가 약 78 %입니다. GKL 규칙은 복잡하지 않습니다.

  • 3의 반지름은 셀의 새로운 상태가 7 개의 이전 셀 (자체, 오른쪽에 3 개의 셀, 왼쪽에있는 3 개의 셀)에 의존한다는 것을 의미합니다.
  • 셀이 현재 O인 경우 새 상태는 대부분 자체이며 셀은 왼쪽이고 셀 3은 왼쪽입니다.
  • 셀이 현재 1인 경우 새 상태는 대부분 자체이며 셀은 오른쪽에 있으며 셀 3은 오른쪽에 있습니다.

예를 들면 다음과 같습니다.

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

이 예에서, 셀룰러 오토 마톤은 8> 6으로 정확하게 계산했다. 다른 예는 더 오랜 시간이 걸리고, 그 동안 일부 멋진 패턴을 생성한다. 아래는 내가 임의로 찾은 두 가지 예입니다.

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

다음 단계로 나아 가기

내 인터넷 조사에서 알 수 있듯이, 대부분의 문제에 대한 거의 모든 학술 연구는 2 개 주 CA로 수행되었습니다. 이 과제에서는 대부분의 문제를 3 개 상태 CA 로 확장 할 것 입니다. 나는 이것을 복수 문제 라고 부를 것이다 . 복수 또는 상대적 과반수는 옵션 중 하나가 각 대안보다 더 많은 투표권을 갖지만 반드시 모든 투표의 과반수 인 것은 아닙니다.

문제 설명

  1. 반경이 3 인 3 상태 1D 셀룰러 오토 마톤이 있습니다.
  2. 거기 (151 개) 는 원형 경계 조건과 세포.
  3. 이들 셀은 단독 상태에서, 3 개 상태 중 1 개가 엄격한 복수를 갖는 단독 시작 상태가된다. "임의의"는 각 셀에 대한 독립적 인 균일 분포를 의미합니다.
  4. 규칙의 정확도는 모든 셀이 10000 세대 내 에서 올바른 상태 (복수의 상태)와 동기화되는 (유효한) 임의 초기 조건의 백분율입니다 .
  5. 목표는 정확도가 높은 규칙을 찾는 것입니다.

복수의 경우 : 50/50/51의 모든 구성은 유효한 시작 구성 (엄격한 복수가 있으므로)이고 51/51/49의 모든 구성은 유효하지 않습니다 (엄격한 복수가 없기 때문에).

검색 공간은 3 ^ 3 ^ 7 (~ 3e1043)이며 전체 검색 범위를 훨씬 벗어납니다. 즉,이 문제를 해결하려면 유전자 알고리즘과 같은 다른 기술을 사용해야합니다. 또한 인간 공학도 필요합니다.

10000 세대 규칙은 사람들이 찾은 규칙의 런타임 / 정확도에 따라 변경 될 수 있습니다. 합리적 수렴 속도를 허용하기에 너무 낮은 경우,이를 높일 수 있습니다. 또는 타이 브레이커 역할을하도록 낮출 수 있습니다.

승리

승자는 모든 참가자 중에서 가장 높은 정확도로 반경 3 CA 규칙을 제출 한 사람입니다.

제출 내용은 다음과 같습니다.

  • 규칙 설명 ( 필요한 경우 Wolfram 코드 사용 )
  • 정확도 및 샘플 크기
  • 규칙을 해결하기 위해 작성한 프로그램 또는 "수동"엔지니어링을 포함하여 규칙을 발견 한 방법에 대한 합리적인 규모의 설명 . (다른 모든 것은 단지 숫자이기 때문에 이것은 가장 흥미로운 부분입니다.)

이전 작업

  • Juille과 Pollack 의 논문은 86 % 정확도로 2 상태 규칙을 어떻게 발전 시켰는지 설명합니다.
  • 이 논문 은 r = 3, 149 셀, 2 상태 CA를 사용했다. 그러나 대부분의 문제를 해결하려고 시도하는 것이 아니라 교대로 모든 패턴 을 신속하게 발생시키는 규칙을 찾는 10입니다. 이러한 차이점에도 불구하고 많은 기술이 비슷할 것으로 생각됩니다.
  • Wolz와 de Oliviera 가 현재 2 개 상태의 기록을 보유하고있는 A (페이 월이 뒤져 있기 때문에 별 도움이되지 않음)

나는 이것이 Plurality 투표 와 관련이 없다는 것을 알게되어 매우 실망했습니다 .
고양이

2
@cat 실제로는 느낌이 듭니다. 각 셀의 상태는 "투표"(3 명 중 1 명 중에서 선택)를 나타낼 수 있으며, 목표는 선거의 승자를 결정하는 것입니다.
PhiNotPi

2
이것은 흥미로운 코드 도전입니다. 나는 골퍼가 아니므로 항상 이런 종류의 퍼즐을 보는 것은 즐거움입니다.
Draco18s

답변:


6

GKL 플러스 언덕 오르기, 61.498 %

  • 셀이 0이면 셀 3을 왼쪽, 1을 왼쪽 및 자체를보십시오. 값을 다수로 설정하십시오. 동점이라면 그대로 유지하십시오.

  • 셀이 1이면 셀 3을 오른쪽, 1을 오른쪽을 봅니다. 값을 다수로 설정하십시오. 동점이라면 그대로 유지하십시오.

  • 셀이 2 인 경우 셀 2를 왼쪽으로, 2를 오른쪽으로, 3을 오른쪽으로보십시오. 값을 다수로 설정하십시오. 동점이라면 그대로 유지하십시오.

나는 총 100000에서 59453을 얻었습니다. 59.453 %

일부 돌연변이 및 언덕 등반은 61498/100000 = 61.498 %를 초래했습니다.

아마 조금 더 테스트하고 나중에 더 많은 정보를 게시 할 것입니다.


3
사람들이 확인할 수 있도록 실제 61.498 % 규칙을 포함해야합니다.
Martin Ender

아마도 당신이 할 테스트를해야 할 것입니다.
Outgolfer Erik

5

"2를 버리고 GKL을하라"-55.7 %

좋은 규칙이 무엇인지 추측하기는 쉽지 않으므로 적어도 1/3 이상의 점수를 얻는 것을 시도했습니다. 이 전략은 대다수의 주가 0 또는 1 일 때 정답을 얻으려고 노력하고, 2 인 경우 손실을 받아들이는 것입니다. 10 만 건의 시험에서 56.5 %의 점수를 얻었습니다. 78 %를 곱하여 예상했던 것보다 GKL의 점수) * 2/3 (답이 0 또는 1 인 시간의 비율) = 52 %.

보다 구체적으로 전략은 다음과 같습니다.

  • 셀이 0 또는 1 인 경우 GKL 전략에서와 같이 3 개의 셀 중 대부분을 취하지 만 2 인 이웃은 무시하십시오. 동점 인 경우 셀을 변경하지 마십시오.
  • 셀이 2 인 경우 전체 이웃에서 0 또는 1 중 더 많은 수를 선택하십시오. 동점 인 경우 맨 왼쪽 값인 0 또는 1을 선택하십시오. 모든 이웃이 2 인 경우 2를 유지하십시오.

이 코드를 사용하여 테스트했습니다.

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

점수는 생성 한계에 따라 증가하기 때문에 예상보다 높습니다. GKL의 78 % 점수는 실제로 수백 세대 정도의 매우 작은 한계에 해당합니다. 대조적으로, 10,000 개의 유전자는 GKL의 정확도를 높여 줄 것입니다. 아마도 결과와 일치 할 것입니다.
PhiNotPi

2

"가장 좋은 것을 훔치고 진화 시키십시오", bleh

편집 : 현재 상태 에서이 답변은 더 나은 패턴을 찾는 것이 아니라 더 나은 무작위 샘플을 찾습니다.

이 답변은 모든 상태를 3 진수 (최하위 숫자)로 열거하여 솔루션을 인코딩 / 디코딩합니다. 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

이 답변은 다음 코드를 사용하여 feersum의 55.7 %에서 발전했습니다. 이 코드에는 개인 C ++ 헤더 전용 라이브러리 인 libop 이 필요합니다 . git clone https://github.com/orlp/libop프로그램을 저장 한 디렉토리와 같은 디렉토리에 설치하기가 매우 쉽습니다 . 로 컴파일하는 것이 좋습니다 g++ -O2 -m64 -march=native -std=c++11 -g. 빠른 개발을 위해 위의 명령을 실행하여 사전 컴파일 libop을 제안합니다 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

핸드 코딩, 57.541 %

실제로는 위의 5 개 셀만 봅니다. 보이는 범위를 늘려서 아마 향상 될 수 있습니다. 100,000 개의 테스트 사례로 실행했습니다.

연산:

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

유전자:

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

테스트 코드 :

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.