C ++ 11, 6-8 minutos
Minha execução de teste leva de 6 a 8 minutos na minha máquina Fedora 19, i5. Mas devido à aleatoriedade da mutação, pode muito bem ser mais rápido ou levar mais tempo do que isso. Eu acho que os critérios de pontuação precisam ser reajustados.
Imprime o resultado como texto no final da conclusão, pessoa íntegra denotada por ponto ( .
), pessoa infectada por asterisco ( *
), a menos que o ANIMATE
sinalizador esteja definido como verdadeiro; nesse caso, ele exibirá caracteres diferentes para pessoas infectadas com diferentes tipos de vírus.
Aqui está um GIF para 10x10, 200 períodos.
Comportamento de mutação
Cada mutação dará uma nova cepa nunca vista antes (portanto, é possível que uma pessoa infecte as quatro pessoas vizinhas com 4 cepas distintas), a menos que 800 cepas tenham sido geradas, caso em que nenhum vírus sofrerá mais mutação.
O resultado de 8 minutos vem do seguinte número de pessoas infectadas:
Período 0, infectado: 4
Período 100, infectado: 53743
Período 200, infectado: 134451
Período 300, infectado: 173369
Período 400, infectado: 228176
Período 500, infectado: 261473
Período 600, infectado: 276086
Período 700, infectado: 265774
Período 800, infectado: 236828
Período 900, infectado: 221275
enquanto o resultado de 6 minutos vem do seguinte:
Período 0, infectado: 4
Período 100, infectado: 53627
Período 200, infectado: 129033
Período 300, infectado: 186127
Período 400, infectado: 213633
Período 500, infectado: 193702
Período 600, infectado: 173995
Período 700, infectado: 157966
Período 800, infectado: 138281
Período 900, infectado: 129381
Representação de pessoa
Cada pessoa é representada em 205 bytes. Quatro bytes para armazenar o tipo de vírus que essa pessoa está contraindo, um byte para armazenar por quanto tempo essa pessoa está infectada e 200 bytes para armazenar quantas vezes ele contraiu cada cepa de vírus (2 bits cada). Talvez haja algum alinhamento adicional de bytes feito pelo C ++, mas o tamanho total será de cerca de 200 MB. Eu tenho duas grades para armazenar a próxima etapa; portanto, ele usa cerca de 400 MB.
Eu armazeno o local das pessoas infectadas em uma fila, para reduzir o tempo necessário nos primeiros períodos (o que é realmente útil até períodos <400).
Detalhes técnicos do programa
A cada 100 etapas, este programa imprime o número de pessoas infectadas, a menos que o ANIMATE
sinalizador esteja definido true
, nesse caso, imprime a grade inteira a cada 100 ms.
Isso requer bibliotecas C ++ 11 (compile usando -std=c++11
sinalizador ou no Mac com clang++ -std=c++11 -stdlib=libc++ virus_spread.cpp -o virus_spread
).
Execute-o sem argumentos para os valores padrão ou com argumentos como este:
./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;
}