C ++ 11, 6-8 minuti
Il mio test dura circa 6-8 minuti nella mia macchina Fedora 19, i5. Ma a causa della casualità della mutazione, potrebbe anche essere più veloce o richiedere più tempo. Penso che i criteri di punteggio debbano essere rivisitati.
Stampa il risultato come testo al termine del completamento, persona sana indicata da punto ( .
), persona infetta da asterisco ( *
), a meno che la ANIMATE
bandiera non sia impostata su true, nel qual caso mostrerà caratteri diversi per le persone infette da un diverso ceppo virale.
Ecco una GIF per 10x10, 200 periodi.
Comportamento mutazione
Ogni mutazione produrrà un nuovo ceppo mai visto prima (quindi è possibile che una persona contagini le quattro persone vicine con 4 ceppi distinti), a meno che non siano stati generati 800 ceppi, nel qual caso nessun virus andrà in ulteriore mutazione.
Il risultato di 8 minuti proviene dal seguente numero di persone infette:
Periodo 0, infetto: 4
Periodo 100, infetto: 53743
Periodo 200, infetto: 134451
Periodo 300, infetto: 173369
Periodo 400, infetto: 228176
Periodo 500, infetto: 261473
Periodo 600, infetto: 276086
Periodo 700, infetto: 265774
Periodo 800, infetto: 236828
Periodo 900, infetto: 221275
mentre il risultato di 6 minuti deriva da quanto segue:
Periodo 0, infetto: 4
Periodo 100, infetto: 53627
Periodo 200, infetto: 129033
Periodo 300, infetto: 186127
Periodo 400, infetto: 213633
Periodo 500, infetto: 193702
Periodo 600, infetto: 173995
Periodo 700, infetto: 157966
Periodo 800, infetto: 138281
Periodo 900, infetto: 129381
Rappresentazione della persona
Ogni persona è rappresentata in 205 byte. Quattro byte per memorizzare il tipo di virus che questa persona sta contraendo, un byte per memorizzare da quanto tempo questa persona è stata infettata e 200 byte per memorizzare quante volte ha contratto ogni ceppo di virus (2 bit ciascuno). Forse c'è qualche ulteriore allineamento di byte fatto da C ++, ma la dimensione totale sarà di circa 200 MB. Ho due griglie per memorizzare il passaggio successivo, quindi in totale utilizza circa 400 MB.
Conservo la posizione delle persone infette in una coda, per ridurre il tempo richiesto nei primi periodi (che è davvero utile fino a periodi <400).
Tecnici del programma
Ogni 100 passaggi questo programma stampa il numero di persone infette, a meno ANIMATE
che non sia impostato flag true
, nel qual caso stampa l'intera griglia ogni 100 ms.
Ciò richiede librerie C ++ 11 (compilare usando -std=c++11
flag o in Mac con clang++ -std=c++11 -stdlib=libc++ virus_spread.cpp -o virus_spread
).
Eseguilo senza argomenti per i valori predefiniti o con argomenti come questo:
./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;
}