С ++ 11, 6-8 минут
Мой тестовый прогон занимает около 6-8 минут на моей машине Fedora 19, i5. Но из-за случайности мутации, это также может быть быстрее или занять больше времени. Я думаю, что критерии оценки должны быть переадресованы.
Печатает результат в виде текста в конце завершения, здорового человека, обозначенного точкой ( .
), зараженного человека звездочкой ( *
), если для ANIMATE
флага не установлено значение true, и в этом случае он будет отображать разные символы для людей, зараженных различным штаммом вируса.
Вот GIF для 10x10, 200 периодов.
Мутационное поведение
Каждая мутация даст новый штамм, который никогда не наблюдался ранее (поэтому возможно, что один человек заразит четырех соседних людей четырьмя различными штаммами), если только не было получено 800 штаммов, и в этом случае ни один вирус не будет подвергаться дальнейшей мутации.
8-минутный результат получается из следующего числа зараженных людей:
Период 0, Заражено: 4
Период 100, Заражено: 53743
Период 200, Заражено: 134451
Период 300, Заражено: 173369
Период 400, Заражено: 228176
Период 500, Заражено: 261473
Период 600, Заражено: 276086
Период 700, Заражено: 265774
Период 800, Заражено: 236828
Период 900, Заражено: 221275
в то время как 6-минутный результат получается из следующего:
Период 0, Заражено: 4
Период 100, Заражено: 53627
Период 200, Заражено: 129033
Период 300, Заражено: 186127
Период 400, Заражено: 213633
Период 500, Заражено: 193702
Период 600, Заражено: 173995
Период 700, заражено: 157966
Период 800, Заражено: 138281
Период 900, Заражено: 129381
Личное представление
Каждый человек представлен в 205 байтах. Четыре байта для хранения типа вируса, с которым заражается этот человек, один байт для хранения информации о том, как долго этот человек был заражен, и 200 байтов для хранения информации о том, сколько раз он заразился каждым штаммом вируса (по 2 бита). Возможно, в C ++ есть дополнительное выравнивание байтов, но общий размер будет около 200 МБ. У меня есть две сетки для хранения следующего шага, поэтому в общей сложности он использует около 400 МБ.
Я сохраняю местонахождение зараженных людей в очереди, чтобы сократить время, необходимое в ранние периоды (что действительно полезно до периодов <400).
Программные особенности
Каждые 100 шагов эта программа будет печатать количество зараженных людей, если не установлен ANIMATE
флаг true
, и в этом случае она будет печатать всю сетку каждые 100 мс.
Для этого требуются библиотеки C ++ 11 (скомпилируйте с использованием -std=c++11
флага или в Mac с помощью clang++ -std=c++11 -stdlib=libc++ virus_spread.cpp -o virus_spread
).
Запустите его без аргументов для значений по умолчанию или с такими аргументами:
./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;
}