C ++ 11, 6-8 minut
Mój test trwa około 6-8 minut na moim komputerze Fedora 19, i5. Ale z powodu losowości mutacji, równie dobrze może ona być szybsza lub potrwać dłużej. Myślę, że kryteria punktacji wymagają ponownego sformułowania.
Drukuje wynik jako tekst na końcu zakończenia, zdrowa osoba oznaczona kropką ( .
), zainfekowana osoba gwiazdką ( *
), chyba że ANIMATE
flaga jest ustawiona na true, w takim przypadku wyświetla różne znaki dla osób zainfekowanych różnymi szczepami wirusów.
Oto GIF na 10x10, 200 okresów.
Zachowanie mutacyjne
Każda mutacja da nowy szczep, którego nigdy wcześniej nie widziano (więc możliwe jest, że jedna osoba zaraża czterech sąsiadujących ludzi 4 różnymi szczepami), chyba że wytworzono 800 szczepów, w którym to przypadku żaden wirus nie przejdzie na żadną dalszą mutację.
8-minutowy wynik pochodzi z następującej liczby zainfekowanych osób:
Okres 0, Zarażony: 4
Okres 100, Zarażony: 53743
Okres 200, Zarażony: 134451
Okres 300, zainfekowany: 173369
Okres 400, Zarażony: 228176
Okres 500, Zarażony: 261473
Okres 600, zainfekowany: 276086
Okres 700, Zarażony: 265774
Okres 800, Zarażony: 236828
Okres 900, zainfekowany: 221275
podczas gdy wynik 6 minut pochodzi z:
Okres 0, Zarażony: 4
Okres 100, Zarażony: 53627
Okres 200, Zarażony: 129033
Okres 300, Zarażony: 186127
Okres 400, Zarażony: 213633
Okres 500, Zarażony: 193702
Okres 600, zainfekowany: 173995
Okres 700, Zarażony: 157966
Okres 800, Zarażony: 138281
Okres 900, Zarażony: 129381
Reprezentacja osoby
Każda osoba jest reprezentowana w 205 bajtach. Cztery bajty do przechowywania typu wirusa, który ta osoba się zaraża, jeden bajt do przechowywania informacji o tym, jak długo ta osoba została zainfekowana, oraz 200 bajtów do przechowywania informacji o tym, ile razy zarażał każdy szczep wirusa (2 bity każdy). Być może C ++ wykonuje dodatkowe wyrównanie bajtów, ale całkowity rozmiar wyniesie około 200 MB. Mam dwie siatki do przechowywania następnego kroku, więc w sumie zużywa około 400 MB.
Przechowuję lokalizację zainfekowanych osób w kolejce, aby skrócić czas wymagany we wczesnych okresach (co jest naprawdę przydatne do okresów <400).
Dane techniczne programu
Co 100 kroków ten program wypisze liczbę zainfekowanych osób, chyba że ANIMATE
ustawiono flagę true
, w którym to przypadku drukuje całą siatkę co 100 ms.
Wymaga to bibliotek C ++ 11 (kompilacja przy użyciu -std=c++11
flagi lub w systemie Mac z clang++ -std=c++11 -stdlib=libc++ virus_spread.cpp -o virus_spread
).
Uruchom go bez argumentów dla wartości domyślnych lub z argumentami takimi jak ten:
./virus_spread 1 0.01 1000
#include <cstdio>
#include <cstring>
#include <random>
#include <cstdlib>
#include <utility>
#include <iostream>
#include <deque>
#include <cmath>
#include <functional>
#include <unistd.h>
typedef std::pair<int, int> pair;
typedef std::deque<pair> queue;
const bool ANIMATE = false;
const int MY_RAND_MAX = 999999;
std::default_random_engine generator(time(0));
std::uniform_int_distribution<int> distInt(0, MY_RAND_MAX);
auto randint = std::bind(distInt, generator);
std::uniform_real_distribution<double> distReal(0, 1);
auto randreal = std::bind(distReal, generator);
const int VIRUS_TYPE_COUNT = 800;
const int SIZE = 1000;
const int VIRUS_START_COUNT = 4;
typedef struct Person{
int virusType;
char time;
uint32_t immune[VIRUS_TYPE_COUNT/16];
} Person;
Person people[SIZE][SIZE];
Person tmp[SIZE][SIZE];
queue infecteds;
double transmissionProb = 1.0;
double mutationProb = 0.01;
int periods = 1000;
char inline getTime(Person person){
return person.time;
}
char inline getTime(int row, int col){
return getTime(people[row][col]);
}
Person inline setTime(Person person, char time){
person.time = time;
return person;
}
Person inline addImmune(Person person, uint32_t type){
person.immune[type/16] += 1 << (2*(type % 16));
return person;
}
bool inline infected(Person person){
return getTime(person) > 0;
}
bool inline infected(int row, int col){
return infected(tmp[row][col]);
}
bool inline immune(Person person, uint32_t type){
return (person.immune[type/16] >> (2*(type % 16)) & 3) == 3;
}
bool inline immune(int row, int col, uint32_t type){
return immune(people[row][col], type);
}
Person inline infect(Person person, uint32_t type){
person.time = 1;
person.virusType = type;
return person;
}
bool inline infect(int row, int col, uint32_t type){
auto person = people[row][col];
auto tmpPerson = tmp[row][col];
if(infected(tmpPerson) || immune(tmpPerson, type) || infected(person) || immune(person, type)) return false;
person = infect(person, type);
infecteds.push_back(std::make_pair(row, col));
tmp[row][col] = person;
return true;
}
uint32_t inline getType(Person person){
return person.virusType;
}
uint32_t inline getType(int row, int col){
return getType(people[row][col]);
}
void print(){
for(int row=0; row < SIZE; row++){
for(int col=0; col < SIZE; col++){
printf("%c", infected(row, col) ? (ANIMATE ? getType(row, col)+48 : '*') : '.');
}
printf("\n");
}
}
void move(){
for(int row=0; row<SIZE; ++row){
for(int col=0; col<SIZE; ++col){
people[row][col] = tmp[row][col];
}
}
}
int main(const int argc, const char **argv){
if(argc > 3){
transmissionProb = std::stod(argv[1]);
mutationProb = std::stod(argv[2]);
periods = atoi(argv[3]);
}
int row, col, size;
uint32_t type, newType=0;
char time;
Person person;
memset(people, 0, sizeof(people));
for(int row=0; row<SIZE; ++row){
for(int col=0; col<SIZE; ++col){
people[row][col] = {};
}
}
for(int i=0; i<VIRUS_START_COUNT; i++){
row = randint() % SIZE;
col = randint() % SIZE;
if(!infected(row, col)){
infect(row, col, 0);
} else {
i--;
}
}
move();
if(ANIMATE){
print();
}
for(int period=0; period < periods; ++period){
size = infecteds.size();
for(int i=0; i<size; ++i){
pair it = infecteds.front();
infecteds.pop_front();
row = it.first;
col = it.second;
person = people[row][col];
time = getTime(person);
if(time == 0) continue;
type = getType(person);
if(row > 0 && randreal() < transmissionProb){
if(newType < VIRUS_TYPE_COUNT-1 && randreal() < mutationProb){
newType++;
if(!infect(row-1, col, newType)) newType--;
} else {
infect(row-1, col, type);
}
}
if(row < SIZE-1 && randreal() < transmissionProb){
if(newType < VIRUS_TYPE_COUNT-1 && randreal() < mutationProb){
newType++;
if(!infect(row+1, col, newType)) newType--;
} else {
infect(row+1, col, type);
}
}
if(col > 0 && randreal() < transmissionProb){
if(newType < VIRUS_TYPE_COUNT-1 && randreal() < mutationProb){
newType++;
if(!infect(row, col-1, newType)) newType--;
} else {
infect(row, col-1, type);
}
}
if(col < SIZE-1 && randreal() < transmissionProb){
if(newType < VIRUS_TYPE_COUNT-1 && randreal() < mutationProb){
newType++;
if(!infect(row, col+1, newType)) newType--;
} else {
infect(row, col+1, type);
}
}
time += 1;
if(time == 4) time = 0;
person = setTime(person, time);
if(time == 0){
person = addImmune(person, type);
} else {
infecteds.push_back(std::make_pair(row, col));
}
tmp[row][col] = person;
}
if(!ANIMATE && period % 100 == 0) printf("Period %d, Size: %d\n", period, size);
move();
if(ANIMATE){
printf("\n");
print();
usleep(100000);
}
}
if(!ANIMATE){
print();
}
return 0;
}