การออกเสียงลงคะแนนส่วนใหญ่ด้วย Cellular Automata


31

มีปัญหาที่สำคัญจริงๆในเซลออโตมาตาที่เรียกว่าปัญหาส่วนใหญ่ :

ปัญหาส่วนใหญ่หรืองานการจำแนกความหนาแน่นเป็นปัญหาของการค้นหากฎการเคลื่อนที่อัตโนมัติแบบมิติเดียวที่ดำเนินการลงคะแนนเสียงส่วนใหญ่ได้อย่างแม่นยำ

...

เมื่อกำหนดค่าของเซลล์ออโตมาตาสองสถานะด้วยเซลล์ i + j ทั้งหมดซึ่ง i อยู่ในสถานะเป็นศูนย์และ j ซึ่งอยู่ในสถานะเดียวทางออกที่ถูกต้องสำหรับปัญหาการลงคะแนนจะต้องตั้งค่าเซลล์ทั้งหมดให้เป็นศูนย์ในที่สุด i> j และต้องตั้งค่าเซลล์ทั้งหมดให้เป็นหนึ่งถ้า i <j สถานะสุดท้ายที่ต้องการจะไม่ระบุหาก 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 สถานะของรัฐ ในความท้าทายนี้เราจะไปขยายปัญหาส่วนใหญ่ที่จะCAs 3 รัฐ ผมจะเรียกสิ่งนี้ว่าปัญหาส่วนใหญ่ เสียงข้างมากหรือเสียงข้างมากหมายถึงเงื่อนไขที่หนึ่งในตัวเลือกที่มีคะแนนเสียงมากกว่าแต่ละตัวเลือก แต่ไม่จำเป็นต้องเป็นเสียงส่วนใหญ่ของคะแนนทั้งหมด

คำชี้แจงปัญหา

  1. มีหุ่นยนต์เซลลูล่าร์ 1D แบบ 3 สถานะพร้อมรัศมี 3
  2. มี151เซลล์ที่มีเงื่อนไขขอบเขตวงกลม
  3. เซลล์เหล่านี้จะได้รับสถานะเริ่มต้นแบบสุ่มโดยมีเงื่อนไขเพียงอย่างเดียวว่า 1 ใน 3 สถานะมีส่วนใหญ่อย่างเข้มงวด "สุ่ม" หมายถึงการกระจายชุดอิสระสำหรับแต่ละเซลล์
  4. ความถูกต้องของกฎคือเปอร์เซ็นต์ของ (ที่ถูกต้อง) เงื่อนไขเริ่มต้นสุ่มที่ทั้งหมดของเซลล์ประสานไปยังสถานะที่ถูกต้อง (อย่างใดอย่างหนึ่งที่มีจำนวนมาก) ภายใน 10000 ชั่วอายุคน
  5. เป้าหมายคือการหากฎที่มีความแม่นยำสูง

กรณีขอบจำนวนมาก: การกำหนดค่าใด ๆ ที่มี 50/50/51 เป็นการกำหนดค่าเริ่มต้นที่ถูกต้อง (เนื่องจากมีจำนวนมากอย่างเข้มงวด) ในขณะที่การกำหนดค่าใด ๆ ที่มี 51/51/49 ไม่ถูกต้อง (เนื่องจากไม่มีส่วนใหญ่ที่เข้มงวด)

พื้นที่การค้นหาคือ 3 ^ 3 ^ 7 (~ 3e1043) ซึ่งอยู่ไกลเกินเอื้อมของการค้นหาที่ละเอียดถี่ถ้วน ซึ่งหมายความว่าคุณจะต้องใช้เทคนิคอื่น ๆ เช่นอัลกอริทึมทางพันธุกรรมเพื่อแก้ไขปัญหานี้ มันจะต้องใช้วิศวกรรมของมนุษย์

กฎการสร้าง 10000 อาจมีการเปลี่ยนแปลงขึ้นอยู่กับ runtimes / ความถูกต้องของกฎที่ผู้คนพบ ถ้ามันต่ำเกินไปที่จะให้อัตราการบรรจบกันที่สมเหตุสมผล อีกวิธีหนึ่งฉันสามารถลดมันเพื่อใช้เป็นเบรกเกอร์

การชนะ

ผู้ชนะคือผู้ที่ส่งกฏ radius-3 CA ด้วยความแม่นยำสูงสุดจากผู้เข้าแข่งขันทั้งหมด

การส่งของคุณควรรวมถึง ...

  • คำอธิบายของกฎ (ใช้รหัส Wolframหากจำเป็น)
  • อัตราความแม่นยำและขนาดตัวอย่าง
  • คำอธิบายขนาดที่สมเหตุสมผลเกี่ยวกับวิธีที่คุณค้นพบกฎรวมถึงโปรแกรมที่คุณเขียนเพื่อแก้ปัญหาหรือวิศวกรรม "ด้วยตนเอง" (นี่เป็นส่วนที่น่าสนใจที่สุดเพราะทุกอย่างเป็นเพียงตัวเลขดิบ)

งานก่อนหน้า

  • กระดาษโดยJuille และ Pollackอธิบายวิธีการพัฒนากฎ 2 สถานะด้วยความแม่นยำ 86%
  • กระดาษนี้ใช้ r = 3, 149 เซลล์, 2 สถานะของ CA มันไม่ได้พยายามที่จะแก้ปัญหาส่วนใหญ่ แต่แทนที่จะหาก1ฎที่ทำให้เกิด0รูปแบบสลับกันทั้งหมดอย่างรวดเร็ว แม้จะมีความแตกต่างเหล่านี้ แต่ฉันสงสัยว่าหลายเทคนิคจะคล้ายกัน
  • A (ไม่ค่อยมีประโยชน์เพราะอยู่หลังหนังสือพิมพ์จ่ายเงิน) โดยWolz และ de Olivieraซึ่งปัจจุบันมีสถิติ 2 รัฐ

ฉันรู้สึกผิดหวัง / ประหลาดใจมากที่พบว่านี่ไม่มีอะไรเกี่ยวข้องกับการออกเสียงลงคะแนนส่วนใหญ่
แมว

2
@cat ฉันรู้สึกเหมือนจริง แต่ละเซลล์ของรัฐจะเป็นตัวแทนของ "โหวต" (เลือก 1 ใน 3 ของผู้สมัคร) และเป้าหมายคือการกำหนดผู้ชนะการเลือกตั้ง
PhiNotPi

2
นี่คือความท้าทายรหัสที่น่าสนใจ ฉันไม่ใช่นักกอล์ฟดังนั้นมันจึงเป็นความสุขที่ได้เห็นปริศนาประเภทนี้
Draco18s

คำตอบ:


6

ประเภทของ GKL บวกปีนเขา 61.498%

  • หากเซลล์เป็น 0 ให้ดูที่เซลล์ 3 ทางด้านซ้าย 1 ทางด้านซ้ายและที่ตัวเอง กำหนดค่าเป็นเสียงส่วนใหญ่ ถ้ามันเป็นเน็คไทจงอยู่ในแบบที่คุณเป็น

  • หากเซลล์เป็น 1 ให้ดูที่เซลล์ 3 ทางขวา 1 ไปทางขวาและที่เซลล์นั้น กำหนดค่าเป็นเสียงส่วนใหญ่ ถ้ามันเป็นเน็คไทจงอยู่ในแบบที่คุณเป็น

  • หากเซลล์เป็น 2 ให้ดูที่เซลล์ 2 ทางด้านซ้าย 2 ทางด้านขวาและ 3 ทางด้านขวา กำหนดค่าเป็นเสียงส่วนใหญ่ ถ้ามันเป็นเน็คไทจงอยู่ในแบบที่คุณเป็น

ฉันได้ 59453 จากจำนวนทั้งหมด 100,000, 59.453%

การกลายพันธุ์และการปีนเขาบางรายการส่งผลให้ 61498/100000 = 61.498%

ฉันจะทดสอบอีกเล็กน้อยและจะโพสต์ข้อมูลเพิ่มเติมในภายหลัง


3
คุณควรรวมกฎที่แท้จริง 61.498% เพื่อให้ผู้คนสามารถยืนยันได้
Martin Ender

คุณควรทำแบบทดสอบที่คุณทำ
Erik the Outgolfer

5

"เพิ่งจะโยน 2s และทำ GKL" - 55.7%

มันไม่ง่ายเลยที่จะเดาว่ากฎที่ดีจะเป็นอย่างไรดังนั้นอย่างน้อยฉันก็พยายามหาสิ่งที่มีคะแนนมากกว่า 1/3 กลยุทธ์คือการพยายามหาคำตอบที่ถูกต้องเมื่อรัฐส่วนใหญ่เป็น 0 หรือ 1 และยอมรับการสูญเสียถ้าเป็น 2 มันได้คะแนน 56.5% จากการทดลอง 100,000 ครั้งซึ่งค่อนข้างดีกว่าที่คาดไว้เล็กน้อยจากการคูณ 78% ( คะแนน GKL) * 2/3 (เศษส่วนของเวลาเมื่อคำตอบคือ 0 หรือ 1) = 52%

ยิ่งเป็นรูปธรรมมากขึ้นกลยุทธ์ดังต่อไปนี้:

  • หากเซลล์เป็น 0 หรือ 1 ให้ใช้เซลล์ส่วนใหญ่ 3 เซลล์ในกลยุทธ์ GKL แต่ไม่สนใจเพื่อนบ้านที่เป็น 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;
}

คะแนนสูงกว่าที่คุณคาดหวังเพราะจะเพิ่มตามขีด จำกัด การสร้าง คะแนน 78% ของ GKL นั้นจริง ๆ แล้วมีขีด จำกัด เพียงเล็กน้อยเท่านั้นสองสามร้อยวงศ์หรือประมาณนั้น ในทางตรงกันข้าม 10,000 วงศ์จะให้อัตราความแม่นยำสูงกว่า GKL ซึ่งอาจสอดคล้องกับผลลัพธ์ที่คุณได้รับ
PhiNotPi

2

"แค่ขโมยสิ่งที่ดีที่สุดแล้วค่อยพัฒนามัน"

แก้ไข: ในสถานะปัจจุบันคำตอบนี้แทนที่จะค้นหารูปแบบที่ดีกว่าพบตัวอย่างที่ดีขึ้น

คำตอบนี้เข้ารหัส / ถอดรหัสโซลูชั่นโดยการระบุสถานะทั้งหมดเป็นตัวเลขที่ประกอบไปด้วย (อย่างน้อยหลักสำคัญก่อน) ทางออกสำหรับ 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% โดยใช้รหัสต่อไปนี้ รหัสนี้ต้องใช้libopซึ่งเป็นไลบรารีส่วนหัว C ++ ส่วนตัวของฉัน ติดตั้งง่ายมากเพียงทำgit clone https://github.com/orlp/libopในไดเรกทอรีเดียวกับที่คุณบันทึกโปรแกรม g++ -O2 -m64 -march=native -std=c++11 -gผมขอแนะนำให้รวบรวมกับ สำหรับการพัฒนาอย่างรวดเร็วฉันยังขอแนะนำให้ precompiling 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.