¡Sé un epidemiólogo!

13

Desafío

Debe crear un modelo simple de cómo la enfermedad se propaga alrededor de un grupo de personas.

Reglas y requisitos

El modelo debe ser una matriz 2D de 1000 por 1000 y cada elemento debe ser una persona diferente.

El usuario debe ingresar tres variables utilizando argv: la probabilidad de transmisión (la probabilidad de que alguien infecte a otra persona), la probabilidad de mutación y durante cuántos períodos debe ejecutarse la simulación.

En el primer período ( t=0), cuatro personas deben ser elegidas al azar e infectadas con la enfermedad.

La forma en que se comporta la enfermedad se rige por las siguientes reglas:

  • La enfermedad solo puede moverse verticalmente y horizontalmente, pasando a la persona de al lado.
  • La infección dura 3 períodos en cada persona. No puede tener en cuenta las inmunodeficiencias.
  • Después de que una persona ha sido infectada tres veces, es inmune y no puede volver a infectarse.
  • La enfermedad está sujeta a mutaciones, lo que hace que las personas previamente inmunes sean vulnerables a esta nueva enfermedad mutada. La enfermedad mutada tiene exactamente los mismos rasgos y sigue las mismas reglas que la enfermedad original.
  • Si se produce una mutación, toda la enfermedad no cambia, solo ese "paquete" particular tras la transmisión.
  • Una vez que una persona ha sido infectada por un virus, no puede volver a infectarse hasta que pase la infección actual.
  • Si una persona está infectada, es infecciosa desde el comienzo de su período de infección hasta su final.
  • No hay niveles de inmunidad: una persona es inmune o no.
  • Para detener la sobrecarga de memoria, hay un límite máximo de 800 mutaciones.

Al final del número especificado de períodos, debe generar los resultados.

Los resultados deben ser una cuadrícula de 1000 x 1000 que muestre qué personas están infectadas y cuáles no. Esto se puede generar como un archivo de texto, como un archivo de imagen o una salida gráfica (donde #FFFFFF es una persona sana y # 40FF00 es una persona infectada).

¿Puede incluir el nombre del idioma y un comando para ejecutarlo en su respuesta?

Victorioso

El código más rápido para ejecutar en mi computadora gana. Su tiempo se medirá con el siguiente código de Python:

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

Tenga en cuenta que al ejecutar este script, usaré los siguientes valores predeterminados:

Probability of transmission = 1
Chance of mutation = 0.01
Number of periods = 1000
Decaimiento Beta
fuente
3
¿Quieres hacer un archivo de 10 gigabytes ?
Ypnypn
1
Al tener un límite de 4 GB, ha eliminado por completo la opción de guardar la salida en el archivo de imagen ...
Optimizer
10
1000x1000 : ¡Eso es más!
COTO
1
También diga que hay dos personas una al lado de la otra. El primer virus contrae V, el segundo contrae virus V'. La contracción terminará en el mismo período. ¿El virus puede Vinfectar a la segunda persona? (O una pregunta más en blanco y negro: ¿es posible que una persona se infecte inmediatamente después de curarse, por lo que terminará con 6 períodos consecutivos de infección?)
solo el
1
Otro, ¿pueden dos virus independientes mutar al mismo virus? Digamos que tenemos Ven persona A, y Vnuevamente en persona B. Cuando transmiten el virus, ¿pueden ambos mutar a la misma mutación V'? ¿O tal vez de hecho deberían mutar a la misma cepa de virus? Si pueden mutar arbitrariamente, ¿cuál es la probabilidad de que dos virus muten a la misma cepa de virus?
justhalf

Respuestas:

10

Tenía curiosidad de cómo se vería, así que hice este truco rápido y sucio en 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()
Andrés
fuente
1
Establecer infección ¡La probabilidad de 1 hizo algunos de los patrones más dulces que he visto!
William Barbosa
¿Podría agregar cuánto tiempo tarda su programa en responder?
Beta Decay
7

C ++ 11, 6-8 minutos

Mi ejecución de prueba tarda unos 6-8 minutos en mi máquina Fedora 19, i5. Pero debido a la aleatoriedad de la mutación, bien podría ser más rápido o tomar más tiempo que eso. Creo que los criterios de puntuación deben ser reorientados.

Imprime el resultado como texto al final de la finalización, persona sana indicada por punto ( .), persona infectada por asterisco ( *), a menos queANIMATE indicador se establezca en verdadero, en cuyo caso mostrará diferentes caracteres para las personas infectadas con una cepa de virus diferente.

Aquí hay un GIF para 10x10, 200 períodos.

10x10Gif

Comportamiento de mutación

Cada mutación dará una nueva cepa nunca antes vista (por lo que es posible que una persona infecte a las cuatro personas vecinas con 4 cepas distintas), a menos que se hayan generado 800 cepas, en cuyo caso ningún virus continuará con ninguna mutación.

El resultado de 8 minutos proviene del siguiente número de personas 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

mientras que el resultado de 6 minutos proviene de lo siguiente:

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

Representación de la persona

Cada persona está representada en 205 bytes. Cuatro bytes para almacenar el tipo de virus que esta persona está contrayendo, un byte para almacenar cuánto tiempo ha estado infectada esta persona y 200 bytes para almacenar cuántas veces ha contraído cada cepa de virus (2 bits cada uno). Quizás haya una alineación de bytes adicional realizada por C ++, pero el tamaño total será de alrededor de 200 MB. Tengo dos cuadrículas para almacenar el siguiente paso, por lo que en total usa alrededor de 400 MB.

Guardo la ubicación de las personas infectadas en una cola, para reducir el tiempo requerido en los primeros períodos (que es realmente útil hasta períodos <400).

Tecnicismos del programa

Cada 100 pasos, este programa imprimirá el número de personas infectadas, a menos que la ANIMATEmarca esté configurada true, en cuyo caso imprimirá la cuadrícula completa cada 100 ms.

Esto requiere bibliotecas C ++ 11 (compilar usando -std=c++11flag, o en Mac con clang++ -std=c++11 -stdlib=libc++ virus_spread.cpp -o virus_spread).

Ejecútelo sin argumentos para los valores predeterminados, o con 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;
}
justo
fuente
¡Realmente me gusta esto! Mi única pregunta es ¿cómo se hace el GIF?
Beta Decay
1
Estoy usando esta herramienta: linux.die.net/man/1/byzanz-record . Actualmente no tiene GUI, por lo que necesitará usar la línea de comando = D
solo el
Oh, eso es bueno, gracias! :)
Decaimiento Beta
3

C # 6-7 minutos

Editar 2

Finalmente (5 horas) generé una salida detallada durante cerca de 1000 períodos (solo 840 cuadros luego se bloqueó) a 1000x1000, cada 1 período, sin embargo, está cerca de 160 MB y requiere toda la memoria en mi sistema para visualizarse (IrfanView) , ni siquiera estoy seguro de que funcionaría en un navegador, puedo instalarlo más tarde.

EDITAR

He dedicado mucho tiempo a hacer esto más eficiente según la respuesta de "Beta Decay" que dice "Elija la cepa al azar". He elegido solo el método aleatorio para elegir quién infecta a quién por período, sin embargo, he cambiado la forma en que se calcula y enhebrando todo, he actualizado mis publicaciones con los nuevos detalles.

Codifiqué mi estimación más cercana a esto que pude, espero que siga todas las reglas, use una tonelada de memoria en mi sistema (aproximadamente 1.2 GB). El programa puede generar gifs animados (se ve genial, muy lento) o simplemente una imagen que coincida con las especificaciones de "Beta Decay". Esto es un poco reinventar la rueda, pero definitivamente se ve genial:


Resultados

(Nota: esto solo diferencia entre infectados y no infectados, es decir, no detallados)

1000 períodos, 1% de tasa de mutación, 100% de propagación:

Resultado

Ejemplos (detallados)

De todos modos, usar el 100% de "Probabilidad de transmisión" en modo no detallado es algo aburrido, ya que siempre obtienes las mismas formas y no puedes ver las diferentes mutaciones, si ajustas los parámetros alrededor de un bit (y habilitas el modo detallado) obtienes un resultado atractivo (los GIF animados se muestran cada 10 cuadros):

Aleatorio - Tamaño de cuadrícula: 200, transmisión de prob: 100%, mutación de prob: 1%

100Preciente

Aleatorio - Tamaño de cuadrícula: 200, transmisión de prob: 20%, mutación de prob: 1%

20Actual

Puntuación

Estoy de acuerdo con "solo" que los criterios de puntuación pueden no ser justos ya que cada carrera será diferente debido a la aleatoriedad de las mutaciones y la posición de los puntos de inicio aleatorios. Tal vez podríamos hacer un promedio de varias carreras o algo así ... bueno, de todos modos esto está en C #, así que recompensa para mí :( de todos modos.

Código

Asegúrese de incluir la biblioteca MagickImage (configurada para compilar x64 bit) de lo contrario no se compilará ( 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
fuente