「最高のものは何でも盗んで進化させてください」、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%から進化しました。このコードにはlibopが必要です。これは私の個人的なC ++ヘッダー専用ライブラリです。インストールは非常に簡単で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;
}