Seja um epidemiologista!

13

Desafio

Você deve criar um modelo simples de como a doença se espalha por um grupo de pessoas.

Regras e Requisitos

O modelo deve ser uma matriz 2D de 1000 por 1000, com cada elemento sendo uma pessoa diferente.

O usuário deve inserir três variáveis ​​usando argv: probabilidade de transmissão (qual a probabilidade de alguém infectar outra pessoa), chance de mutação e quantos períodos a simulação deve ser executada.

No primeiro período ( t=0), quatro pessoas devem ser escolhidas aleatoriamente e infectadas com a doença.

O modo como a doença se comporta é regido pelas seguintes regras:

  • A doença só pode se mover vertical e horizontalmente, passando para a pessoa ao lado.
  • A infecção dura 3 períodos em todas as pessoas. Você não pode levar em consideração as imunodeficiências.
  • Após uma pessoa ter sido infectada três vezes, ela fica imune e não pode ser infectada novamente.
  • A doença está sujeita a mutações, tornando as pessoas anteriormente imunes vulneráveis ​​a essa nova doença mutada. A doença mutada tem exatamente as mesmas características e segue as mesmas regras da doença original.
  • Se ocorrer uma mutação, toda a doença não muda, apenas esse 'pacote' específico na transmissão.
  • Depois que uma pessoa foi infectada por um vírus, ela não pode ser infectada novamente até que a infecção atual passe.
  • Se uma pessoa está infectada, ela é infecciosa desde o início do período de infecção até o fim.
  • Não há níveis de imunidade - uma pessoa é imune ou não.
  • Para parar a sobrecarga de memória, há um limite máximo de 800 mutações.

No final do número especificado de períodos, você deve exibir os resultados.

Os resultados devem ser uma grade de 1000 x 1000, mostrando quais pessoas estão infectadas e quais não. Isso pode ser produzido como um arquivo de texto, como um arquivo de imagem ou gráfico (onde #FFFFFF é uma pessoa saudável e # 40FF00 é uma pessoa infectada).

Você pode incluir o nome do idioma e um comando para executá-lo em sua resposta.

Ganhando

O código mais rápido a ser executado no meu computador vence. Seu tempo será medido com o seguinte trecho de código Python:

import time, os
start = time.time()
os.system(command)
end = time.time()
print(end-start)

Observe que, ao executar este script, usarei os seguintes padrões:

Probability of transmission = 1
Chance of mutation = 0.01
Number of periods = 1000
Beta Decay
fonte
3
Você quer criar um arquivo de 10 gigabytes ?
Ypnypn
1
Por ter um limite de 4 GB, você removeu completamente a opção de salvar a saída em arquivo de imagem ...
Optimizer
10
1000x1000 : É mais assim!
COTO
1
Diga também que há duas pessoas próximas uma da outra. O primeiro contrai vírus V, o segundo contrai vírus V'. A contração terminará no mesmo período. O vírus pode Vinfectar a segunda pessoa? (Ou uma questão mais preto-e-branco: é possível que uma pessoa ser infectada imediatamente após ele está curado, então ele vai acabar com 6 período consecutivo de infecção?)
justhalf
1
Outro, dois vírus independentes podem sofrer mutação para o mesmo vírus? Digamos que temos Vpessoalmente A, e Vnovamente pessoalmente B. Quando eles transmitem o vírus, os dois podem sofrer a mesma mutação V'? Ou talvez eles de fato devam sofrer mutação para a mesma cepa de vírus? Se eles podem sofrer mutações arbitrárias, qual é a probabilidade de dois vírus sofrerem a mesma estirpe?
justhalf

Respostas:

10

Eu estava curioso para saber como isso seria, então fiz esse hack rápido e sujo no JavaScript: http://jsfiddle.net/andrewmaxwell/r8m54t9c/

// The probability that a healthy cell will be infected by an adjacent infected cell EACH FRAME.
var infectionProbability = 0.2

// The probability that the infection will mutate on transmission EACH FRAME.
var mutationProbability = 0.00001

// The maximum number of times a cell can be infected by the same infection.
var maxInfections = 3

// The number of frames a cell in infected before it becomes healthy again.
var infectionDuration = 3

// The width and heigh of the board
var size = 400

// The number of cells infected at the beginning.
var startingNum = 4

var imageData, // the visual representation of the board
    cells, // array of cells
    infectionCount // counter that is incremented whenever a mutation occurs

// Just some colors. The colors are re-used as the number of mutations increases.
var colors = [[255,0,0],[255,255,0],[0,255,0],[0,255,255],[0,0,255],[255,0,255],[128,0,0],[128,128,0],[0,128,0],[0,128,128],[0,0,128],[128,0,128],[255,128,128],[255,255,128],[128,255,128],[128,255,255],[128,128,255],[255,128,255]
]

// when a cell is infected, it isn't contagious until the next frame
function infect(person, infection){
    person.infect = true
    person.infectionCounts[infection] = (person.infectionCounts[infection] || 0) + 1
    person.currentInfection = infection
}

// when a mutation occurs, it is given a number and the counter is incremented
function mutation(){
    return infectionCount++
}

function reset(){

    cells = []
    infectionCount = 0
    imageData = T.createImageData(size, size)

    // initialize the cells, store them in a grid temporarily and an array for use in each frame
    var grid = []
    for (var i = 0; i < size; i++){
        grid[i] = []
        for (var j = 0; j < size; j++){
            cells.push(grid[i][j] = {
                infectionTime: 0, // how many frames until they are no longer infected, so 0 is healthy
                infectionCounts: [], // this stores how many times the cell has been infected by each mutation
                neighbors: [] // the neighboring cells
            })
        }
    }

    // store the neighbors of each cell, I just want to minimize the work done each frame
    var neighborCoords = [[0,-1],[1,0],[0,1],[-1,0]]
    for (var i = 0; i < size; i++){
        for (var j = 0; j < size; j++){
            for (var n = 0; n < neighborCoords.length; n++){
                var row = i + neighborCoords[n][0]
                var col = j + neighborCoords[n][1]
                if (grid[row] && grid[row][col]){
                    grid[i][j].neighbors.push(grid[row][col])
                }
            }
        }
    }

    // infect the initial cells
    for (var i = 0; i < startingNum; i++){
        infect(cells[Math.floor(cells.length * Math.random())], 0)
    }
}

function loop(){
    requestAnimationFrame(loop)

    // for each cell marked as infected, set its infectionTime
    for (var i = 0; i < cells.length; i++){
        var p = cells[i]
        if (p.infect){
            p.infect = false
            p.infectionTime = infectionDuration
        }
    }

    for (var i = 0; i < cells.length; i++){
        var p = cells[i]

        // for each infected cell, decrement its timer
        if (p.infectionTime){
            p.infectionTime--

            // for each neighbor that isn't infected, if the probability is right and the neighbor isn't immune to that infection, infect it
            for (var n = 0; n < p.neighbors.length; n++){
                var neighbor = p.neighbors[n]
                if (!neighbor.infectionTime && Math.random() < infectionProbability){
                    var infection = Math.random() < mutationProbability ? mutation() : p.currentInfection
                    if (!neighbor.infectionCounts[infection] || neighbor.infectionCounts[infection] < maxInfections){
                        infect(neighbor, infection)
                    }
                }
            }

            // colors! yay!
            var color = colors[p.currentInfection % colors.length]
            imageData.data[4 * i + 0] = color[0]
            imageData.data[4 * i + 1] = color[1]
            imageData.data[4 * i + 2] = color[2]
        } else {
            imageData.data[4 * i + 0] = imageData.data[4 * i + 1] = imageData.data[4 * i + 2] = 0
        }

        imageData.data[4 * i + 3] = 255
    }

    T.putImageData(imageData, 0, 0)
}

// init canvas and go
C.width = C.height = size
T = C.getContext('2d')
reset()
loop()
Andrew
fonte
1
Definir a infecçãoProbability como 1 fez alguns dos padrões mais doces que já vi!
William Barbosa
Você poderia adicionar quanto tempo seu programa leva para sua resposta?
Beta Decay
7

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 ANIMATEsinalizador 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.

10x10Gif

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 ANIMATEsinalizador esteja definido true, nesse caso, imprime a grade inteira a cada 100 ms.

Isso requer bibliotecas C ++ 11 (compile usando -std=c++11sinalizador 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;
}
justhalf
fonte
Eu realmente gosto disso! Minha única pergunta é como você faz o GIF?
Beta Decay
1
Estou usando esta ferramenta: linux.die.net/man/1/byzanz-record . Atualmente, ele não tem GUI, portanto, você precisará usar a linha de comando = D
justhalf 4/04
Oh, que bom, obrigado! :)
Decay Beta
3

C # 6-7 minutos

Editar 2

Finalmente, (5 horas) gerei uma saída detalhada por cerca de 1000 períodos (apenas 840 quadros depois travou) a 1000x1000, a cada 1 período, no entanto, é perto de 160MB e requer toda a memória do meu sistema para exibir (IrfanView) , nem mesmo certo de que isso funcionaria em um navegador, posso colocá-lo mais tarde.

EDITAR

Gastei muito tempo para tornar isso mais eficiente, de acordo com a resposta do "Decaimento beta", afirmando "Escolha a estirpe aleatoriamente". Eu escolhi apenas o método aleatório para escolher quem infecta quem por período, no entanto, mudei a maneira como é calculada e resolvi tudo, atualizei minhas postagens com os novos detalhes.

Codifiquei minha estimativa mais próxima disso, espero que siga todas as regras, use uma tonelada de memória no meu sistema (cerca de 1,2 GB). O programa pode gerar gifs animados (parece legal, muito lento) ou apenas uma imagem correspondente às especificações de "Beta Decay". Isso é um pouco de reinventar a roda, mas definitivamente parece legal:


Resultados

(Nota: isso apenas diferencia entre infectado e não infectado, ou seja, não detalhado)

1000 períodos, 1% de taxa de mutação, 100% de propagação:

Resultado

Exemplos (verboso)

De qualquer forma, usar 100% de "Probabilidade de transmissão" no modo não detalhado é meio chato, pois você sempre obtém as mesmas formas e não pode ver as diferentes mutações, se ajustar os parâmetros um pouco (e ativar o modo detalhado) você obtém uma saída interessante (GIFs animados são exibidos a cada 10 quadros):

Aleatório - Tamanho da grade: 200, ProbTransmission: 100%, ProbMutation: 1%

100Precent

Aleatório - Tamanho da grade: 200, ProbTransmission: 20%, ProbMutation: 1%

20Precente

Pontuação

Eu concordo com "justhalf" que os critérios de pontuação podem não ser justos, pois cada corrida será diferente devido à aleatoriedade das mutações e à posição dos pontos de partida aleatórios. Talvez pudéssemos fazer a média de várias execuções ou algo assim ..., bem, de qualquer maneira isso está em C #, então é uma recompensa para mim :( de qualquer maneira.

Código

Certifique-se de incluir a biblioteca MagickImage (definida para compilar x64 bits), caso contrário ela não será criada ( http://pastebin.com/vEmPF1PM ):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
using ImageMagick;
using System.IO;

namespace Infection
{
    class Program
    {
        #region Infection Options
        private const double ProbabilityOfTransmission = .2;
        private const double ChanceOfMutation = 0.01;
        private const Int16 StageSize = 1000;
        private const Int16 MaxNumberOfMutations = 800;
        private const byte MaxInfectionTime = 3;
        private const byte NumberOfPeopleToRandomlyInfect = 4;
        private static int NumberOfPeriods = 1000;
        #endregion Infection Options

        #region Run Options
        private const bool VerbosMode = false;        
        private const int ImageFrequency = 10;
        #endregion Run Options

        #region Stage        
        private static Int16 MutationNumber = 1;

        private class Person
        {
            public Person()
            {
                PreviousInfections = new Dictionary<Int16, byte>();
                InfectionTime = 0;
                CurrentInfection = 0;
                PossibleNewInfections = new List<short>(4);
            }
            public Dictionary<Int16, byte> PreviousInfections { get; set; }
            public byte InfectionTime { get; set; }
            public Int16 CurrentInfection { get; set; }
            public List<Int16> PossibleNewInfections { get; set; }
        }
        private static Person[][] Stage = new Person[StageSize][];
        #endregion Stage

        static void Main(string[] args)
        {
            DateTime start = DateTime.UtcNow;

            //Initialize stage
            for (Int16 i = 0; i < Stage.Length; i++)
            {
                var tmpList = new List<Person>();
                for (Int16 j = 0; j < Stage.Length; j++)
                    tmpList.Add(new Person());
                Stage[i] = tmpList.ToArray();
            }

            //Randomly infect people
            RandomlyInfectPeople(NumberOfPeopleToRandomlyInfect);

            //Run through the periods(NumberOfPeriods times)
            List<MagickImage> output = new List<MagickImage>();
            while (NumberOfPeriods > 0)
            {
                //Print details(verbose)                
                if (VerbosMode && NumberOfPeriods % ImageFrequency == 0)
                {
                    Console.WriteLine("Current Number: " + NumberOfPeriods);
                    Console.WriteLine("Current Mutation: " + MutationNumber);
                    output.Add(BoardToImage());
                }

                Period();
            }

            //Outputs a Animated Gif(verbose)
            if (VerbosMode)
            {
                ImagesToAnimatedGIF(output.ToArray(), Directory.GetCurrentDirectory() + "\\Output.gif");
                System.Diagnostics.Process.Start(Directory.GetCurrentDirectory() + "\\Output.gif");
            }
            //Only outputs the basic result image matching the specs
            SaveBoardToSimpleImage(Directory.GetCurrentDirectory() + "\\FinalState.gif");

            Console.WriteLine("Total run time in seconds: " + (DateTime.UtcNow - start).TotalSeconds);
            Console.WriteLine("Press enter to exit");
            Console.ReadLine();
        }

        #region Image
        private static void SaveBoardToSimpleImage(string filepath)
        {
            using (Bitmap img = new Bitmap(StageSize, StageSize))
            {
                for (int i = 0; i < img.Width; i++)
                    for (int j = 0; j < img.Height; j++)
                        img.SetPixel(i, j, Stage[i][j].CurrentInfection == 0 ? Color.FromArgb(255, 255, 255) :
                            Color.FromArgb(64, 255, 0));
                img.Save(filepath, ImageFormat.Gif);
            }
        }
        private static MagickImage BoardToImage()
        {
            using (Bitmap img = new Bitmap(StageSize, StageSize))
            {
                for (int i = 0; i < img.Width; i++)
                    for (int j = 0; j < img.Height; j++)
                        img.SetPixel(i, j, Stage[i][j].CurrentInfection == 0 ? Color.White :
                            Color.FromArgb(Stage[i][j].CurrentInfection % 255,
                            Math.Abs(Stage[i][j].CurrentInfection - 255) % 255,
                            Math.Abs(Stage[i][j].CurrentInfection - 510) % 255));
                return new MagickImage(img);
            }
        }
        private static void ImagesToAnimatedGIF(MagickImage[] images, string filepath)
        {
            using (MagickImageCollection collection = new MagickImageCollection())
            {
                foreach (var image in images)
                {
                    collection.Add(image);
                    collection.Last().AnimationDelay = 20;
                }
                collection.Write(filepath);
            }
        }
        #endregion Image

        #region Infection
        private static void Period()
        {
            Infect();
            ChooseRandomInfections();
            IncrementDiseaseProgress();
            Cure();

            NumberOfPeriods--;
        }
        private static void Cure()
        {
            Parallel.For(0, Stage.Length, i =>
            {
                for (Int16 j = 0; j < Stage.Length; j++)
                    if (Stage[i][j].CurrentInfection != 0 && Stage[i][j].InfectionTime == MaxInfectionTime + 1)
                    {
                        //Add disease to already infected list
                        if (Stage[i][j].PreviousInfections.ContainsKey(Stage[i][j].CurrentInfection))
                            Stage[i][j].PreviousInfections[Stage[i][j].CurrentInfection]++;
                        else
                            Stage[i][j].PreviousInfections.Add(Stage[i][j].CurrentInfection, 1);

                        //Cure
                        Stage[i][j].InfectionTime = 0;
                        Stage[i][j].CurrentInfection = 0;
                    }
            });
        }
        private static void IncrementDiseaseProgress()
        {
            Parallel.For(0, Stage.Length, i =>
            {
                for (Int16 j = 0; j < Stage.Length; j++)
                    if (Stage[i][j].CurrentInfection != 0)
                        Stage[i][j].InfectionTime++;
            });
        }
        private static void RandomlyInfectPeople(Int16 numberOfPeopleToInfect)
        {
            var randomList = new List<int>();
            while (randomList.Count() < numberOfPeopleToInfect * 2)
            {
                randomList.Add(RandomGen2.Next(StageSize));
                randomList = randomList.Distinct().ToList();
            }
            while (randomList.Count() > 0)
            {
                Stage[randomList.Last()][randomList[randomList.Count() - 2]].CurrentInfection = MutationNumber;
                Stage[randomList.Last()][randomList[randomList.Count() - 2]].InfectionTime = 1;
                randomList.RemoveAt(randomList.Count() - 2);
                randomList.RemoveAt(randomList.Count() - 1);
            }
        }
        private static void Infect()
        {
            Parallel.For(0, Stage.Length, i =>
            {
                for (Int16 j = 0; j < Stage.Length; j++)
                    InfectAllSpacesAround((short)i, j);
            });
        }
        private static void InfectAllSpacesAround(Int16 x, Int16 y)
        {
            //If not infected or just infected this turn return
            if (Stage[x][y].CurrentInfection == 0 || (Stage[x][y].CurrentInfection != 0 && Stage[x][y].InfectionTime == 0)) return;

            //Infect all four directions(if possible)
            if (x > 0)
                InfectOneSpace(Stage[x][y].CurrentInfection, (short)(x - 1), y);

            if (x < Stage.Length - 1)
                InfectOneSpace(Stage[x][y].CurrentInfection, (short)(x + 1), y);

            if (y > 0)
                InfectOneSpace(Stage[x][y].CurrentInfection, x, (short)(y - 1));

            if (y < Stage.Length - 1)
                InfectOneSpace(Stage[x][y].CurrentInfection, x, (short)(y + 1));
        }
        private static void InfectOneSpace(Int16 currentInfection, Int16 x, Int16 y)
        {
            //If the person is infected, or If they've already been infected "MaxInfectionTime" then don't infect
            if (Stage[x][y].CurrentInfection != 0 || (Stage[x][y].PreviousInfections.ContainsKey(currentInfection) &&
                    Stage[x][y].PreviousInfections[currentInfection] >= MaxInfectionTime)) return;

            //If random is larger than change of transmission don't transmite disease
            if (RandomGen2.Next(100) + 1 > ProbabilityOfTransmission * 100) return;

            //Possible mutate
            if (MutationNumber <= MaxNumberOfMutations && RandomGen2.Next(100) + 1 <= ChanceOfMutation * 100)
                lock (Stage[x][y])
                {
                    MutationNumber++;
                    Stage[x][y].PossibleNewInfections.Add(MutationNumber);
                }
            //Regular infection
            else
                lock (Stage[x][y])
                    Stage[x][y].PossibleNewInfections.Add(currentInfection);

        }
        private static void ChooseRandomInfections()
        {
            Parallel.For(0, Stage.Length, i =>
            {
                for (Int16 j = 0; j < Stage.Length; j++)
                {
                    if (Stage[i][j].CurrentInfection != 0 || !Stage[i][j].PossibleNewInfections.Any()) continue;
                    Stage[i][j].CurrentInfection = Stage[i][j].PossibleNewInfections[RandomGen2.Next(Stage[i][j].PossibleNewInfections.Count)];
                    Stage[i][j].PossibleNewInfections.Clear();
                    Stage[i][j].InfectionTime = 0;
                }
            }
            );
        }
        #endregion Infection
    }

    //Fancy Schmancy new random number generator for threaded stuff, fun times
    //http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx
    public static class RandomGen2
    {
        private static Random _global = new Random();
        [ThreadStatic]
        private static Random _local;

        public static int Next()
        {
            Random inst = _local;
            if (inst == null)
            {
                int seed;
                lock (_global) seed = _global.Next();
                _local = inst = new Random(seed);
            }
            return inst.Next();
        }

        public static int Next(int input)
        {
            Random inst = _local;
            if (inst == null)
            {
                int seed;
                lock (_global) seed = _global.Next();
                _local = inst = new Random(seed);
            }
            return inst.Next(input);
        }
    }
}
David Rogers
fonte