Bądź epidemiologiem!

13

Wyzwanie

Musisz stworzyć prosty model rozprzestrzeniania się choroby wśród grupy ludzi.

Zasady i wymagania

Model musi być tablicą 2D o wymiarach 1000 na 1000, przy czym każdy element jest inną osobą.

Użytkownik musi wprowadzić trzy zmienne za pomocą argv: prawdopodobieństwo przeniesienia (prawdopodobieństwo, że ktoś zarazi kogoś innego), prawdopodobieństwo mutacji i liczbę okresów, przez które symulacja powinna zostać uruchomiona.

W pierwszym okresie ( t=0) cztery osoby powinny zostać wybrane losowo i zakażone chorobą.

Zachowanie się choroby regulują następujące zasady:

  • Choroba może poruszać się tylko pionowo i poziomo, przenosząc się na osobę obok.
  • Zakażenie trwa 3 okresy u każdej osoby. Nie możesz brać pod uwagę niedoborów odporności.
  • Po trzykrotnym zarażeniu osoba jest odporna i nie można jej ponownie zainfekować.
  • Choroba podlega mutacji, dzięki czemu osoby uprzednio odporne są podatne na tę nową zmutowaną chorobę. Zmutowana choroba ma dokładnie te same cechy i podlega tym samym zasadom, co pierwotna choroba.
  • Jeśli dojdzie do mutacji, cała choroba się nie zmienia, tylko ten konkretny „pakiet” po przeniesieniu.
  • Gdy dana osoba zostanie zainfekowana jednym wirusem, nie można jej ponownie zainfekować, dopóki nie minie bieżąca infekcja.
  • Jeśli dana osoba jest zarażona, jest zakaźna od początku okresu zakażenia do jego końca.
  • Nie ma poziomów odporności - osoba jest albo odporna, albo nie.
  • Aby zatrzymać przeciążenie pamięci, istnieje maksymalny limit 800 mutacji.

Pod koniec określonej liczby okresów należy wyświetlić wyniki.

Wynikiem musi być siatka 1000 x 1000 pokazująca, które osoby są zainfekowane, a które nie. Może to być wyprowadzane jako plik tekstowy, jako plik obrazu lub wyjście graficzne (gdzie #FFFFFF to zdrowa osoba, a # 40FF00 to osoba zarażona).

Czy możesz podać nazwę języka i polecenie uruchomienia go w swojej odpowiedzi.

Zwycięski

Wygrywa najszybszy kod do uruchomienia na moim komputerze. Jego czas będzie mierzony za pomocą następującego fragmentu kodu Python:

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

Pamiętaj, że podczas uruchamiania tego skryptu użyję następujących ustawień domyślnych:

Probability of transmission = 1
Chance of mutation = 0.01
Number of periods = 1000
Rozpad beta
źródło
3
Chcesz utworzyć plik o rozmiarze 10 gigabajtów ?
Ypnypn
1
Mając limit 4 GB, całkowicie usunąłeś opcję zapisywania danych wyjściowych w pliku obrazu ...
Optimizer
10
1000x1000 : To bardziej tak!
COTO
1
Powiedz też, że są dwie osoby obok siebie. Pierwszy zaraża wirusem V, drugi zaraża wirusem V'. Oba skurcze zakończą się w tym samym okresie. Czy wirus może Vzarazić drugą osobę? (Lub bardziej czarno-białe pytanie: czy jest możliwe, że dana osoba zostanie zarażona natychmiast po wyleczeniu, więc skończy się 6 kolejnymi
okresami
1
Kolejny, czy dwa niezależne wirusy mogą mutować w tego samego wirusa? Powiedzmy, że mamy Vosobiście Ai Vznowu osobiście B. Czy po przeniesieniu wirusa oba mutują się w tej samej mutacji V'? A może faktycznie powinni mutować do tego samego szczepu wirusa? Jeśli potrafią mutować dowolnie, jakie jest prawdopodobieństwo mutacji dwóch wirusów na ten sam szczep wirusa?
justhalf

Odpowiedzi:

10

Byłem ciekawy, jak to będzie wyglądać, więc zrobiłem ten szybki i brudny hack w 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()
Andrzej
źródło
1
Ustawienie infekcji Prawdopodobieństwo 1 sprawiło, że niektóre z najsłodszych wzorów, jakie widziałem!
William Barbosa
Czy mógłbyś dodać, jak długo twój program zajmuje odpowiedź?
Beta Decay
7

C ++ 11, 6-8 minut

Mój test trwa około 6-8 minut na moim komputerze Fedora 19, i5. Ale z powodu losowości mutacji, równie dobrze może ona być szybsza lub potrwać dłużej. Myślę, że kryteria punktacji wymagają ponownego sformułowania.

Drukuje wynik jako tekst na końcu zakończenia, zdrowa osoba oznaczona kropką ( .), zainfekowana osoba gwiazdką ( *), chyba że ANIMATEflaga jest ustawiona na true, w takim przypadku wyświetla różne znaki dla osób zainfekowanych różnymi szczepami wirusów.

Oto GIF na 10x10, 200 okresów.

10x10Gif

Zachowanie mutacyjne

Każda mutacja da nowy szczep, którego nigdy wcześniej nie widziano (więc możliwe jest, że jedna osoba zaraża czterech sąsiadujących ludzi 4 różnymi szczepami), chyba że wytworzono 800 szczepów, w którym to przypadku żaden wirus nie przejdzie na żadną dalszą mutację.

8-minutowy wynik pochodzi z następującej liczby zainfekowanych osób:

Okres 0, Zarażony: 4
Okres 100, Zarażony: 53743
Okres 200, Zarażony: 134451
Okres 300, zainfekowany: 173369
Okres 400, Zarażony: 228176
Okres 500, Zarażony: 261473
Okres 600, zainfekowany: 276086
Okres 700, Zarażony: 265774
Okres 800, Zarażony: 236828
Okres 900, zainfekowany: 221275

podczas gdy wynik 6 minut pochodzi z:

Okres 0, Zarażony: 4
Okres 100, Zarażony: 53627
Okres 200, Zarażony: 129033
Okres 300, Zarażony: 186127
Okres 400, Zarażony: 213633
Okres 500, Zarażony: 193702
Okres 600, zainfekowany: 173995
Okres 700, Zarażony: 157966
Okres 800, Zarażony: 138281
Okres 900, Zarażony: 129381

Reprezentacja osoby

Każda osoba jest reprezentowana w 205 bajtach. Cztery bajty do przechowywania typu wirusa, który ta osoba się zaraża, jeden bajt do przechowywania informacji o tym, jak długo ta osoba została zainfekowana, oraz 200 bajtów do przechowywania informacji o tym, ile razy zarażał każdy szczep wirusa (2 bity każdy). Być może C ++ wykonuje dodatkowe wyrównanie bajtów, ale całkowity rozmiar wyniesie około 200 MB. Mam dwie siatki do przechowywania następnego kroku, więc w sumie zużywa około 400 MB.

Przechowuję lokalizację zainfekowanych osób w kolejce, aby skrócić czas wymagany we wczesnych okresach (co jest naprawdę przydatne do okresów <400).

Dane techniczne programu

Co 100 kroków ten program wypisze liczbę zainfekowanych osób, chyba że ANIMATEustawiono flagę true, w którym to przypadku drukuje całą siatkę co 100 ms.

Wymaga to bibliotek C ++ 11 (kompilacja przy użyciu -std=c++11flagi lub w systemie Mac z clang++ -std=c++11 -stdlib=libc++ virus_spread.cpp -o virus_spread).

Uruchom go bez argumentów dla wartości domyślnych lub z argumentami takimi jak ten:

./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
źródło
Naprawdę to lubię! Moje jedyne pytanie brzmi: jak zrobić GIF?
Beta Decay
1
Korzystam z tego narzędzia: linux.die.net/man/1/byzanz-record . Obecnie nie ma GUI, więc musisz użyć wiersza polecenia = D
justhalf
Och, to miło, dzięki! :)
Beta Decay
3

C # 6-7 minut

Edytuj 2

W końcu (5 godzin) wygenerowałem wyczerpujący wynik dla prawie 1000 okresów (tylko 840 klatek, a następnie rozbił się) przy 1000 x 1000, co 1 okres, jednak jest to prawie 160 MB i wymaga wyświetlenia całej pamięci w moim systemie (IrfanView) , nawet nie jestem pewien, czy to zadziała w przeglądarce, pozwól mi to później.

EDYTOWAĆ

Spędziłem dużo czasu, aby uczynić to bardziej wydajnym zgodnie z odpowiedzią na „Rozpad Beta” stwierdzającą: „Wybierz szczep losowo”. Mam tylko losową metodę wyboru, kto zaraża, kogo na okres, jednak zmieniłem sposób, który jest obliczany i wszystko załatwiłem, zaktualizowałem swoje posty o nowe szczegóły.

Zakodowałem moje najbliższe oszacowanie do tego, co mogłem, mam nadzieję, że przestrzega wszystkich zasad, wykorzystuje mnóstwo pamięci w moim systemie (około 1,2 GB). Program może wyświetlać animowane gify (wygląda fajnie, naprawdę wolno) lub po prostu obraz pasujący do specyfikacji „Beta Decay”. To trochę nowa koncepcja koła, ale zdecydowanie wygląda świetnie:


Wyniki

(Uwaga: rozróżnia to tylko między zarażonym i niezainfekowanym, tj. Nie gadatliwym)

1000 okresów, 1% wskaźnik mutacji, 100% spread:

Wynik

Przykłady (pełne)

W każdym razie użycie 100% „prawdopodobieństwa transmisji” w trybie nieokreślonym jest nudne, ponieważ zawsze dostajesz te same kształty i nie widzisz różnych mutacji, jeśli poprawisz parametry wokół nieco (i włączysz tryb szczegółowy) masz fajnie wyglądający wynik (animowane pliki GIF są wyświetlane co 10 klatek):

Losowo - Rozmiar siatki: 200, Prob Transmisja: 100%, ProbMutacja: 1%

100 procent

Losowo - Rozmiar siatki: 200, Prob Transmisja: 20%, ProbMutacja: 1%

20 procent

Punktacja

Zgadzam się z „justhalf”, że kryteria punktacji mogą być niesprawiedliwe, ponieważ każdy bieg będzie się różnić z powodu losowości mutacji i pozycji losowych punktów początkowych. Może moglibyśmy zrobić średnio kilka biegów lub coś w tym stylu ... Cóż, w każdym razie jest to w C #, więc dla mnie nagroda :( w każdym razie.

Kod

Pamiętaj, aby dołączyć bibliotekę MagickImage (ustawioną na kompilację bitów x64), w przeciwnym razie nie zostanie ona zbudowana ( 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
źródło