Будь эпидемиологом!

13

Вызов

Вы должны создать простую модель того, как болезнь распространяется вокруг группы людей.

Правила и требования

Модель должна представлять собой двумерный массив 1000 на 1000, причем каждый элемент является отдельным человеком.

Пользователь должен ввести три переменные, используя argv: вероятность передачи (вероятность того, что кто-то заразит кого-то другого), вероятность мутации и сколько периодов симуляции должно пройти.

В первом периоде ( t=0) четыре человека должны быть выбраны случайным образом и заражены этой болезнью.

То, как ведет себя болезнь, регулируется следующими правилами:

  • Болезнь может двигаться только вертикально и горизонтально, приближаясь к человеку по соседству.
  • Инфекция длится 3 периода у каждого человека. Вы не можете учитывать иммунодефициты.
  • После того, как человек был заражен три раза, он становится невосприимчивым и не может быть заражен снова.
  • Болезнь подвержена мутациям, что делает ранее иммунных людей уязвимыми для этой новой мутированной болезни. Мутировавшее заболевание имеет те же черты и следует тем же правилам, что и исходное заболевание.
  • Если происходит мутация, вся болезнь не меняется, только этот конкретный «пакет» при передаче.
  • Как только человек был заражен одним вирусом, он не может быть заражен снова, пока текущая инфекция не пройдет.
  • Если человек заражен, он заразен от начала своего инфекционного периода до его конца.
  • Нет никаких уровней иммунитета - человек или неуязвим или нет.
  • Чтобы остановить перегрузку памяти, существует максимум 800 мутаций.

В конце указанного количества периодов вы должны вывести результаты.

Результаты должны быть сеткой 1000 x 1000, показывающей, какие люди заражены, а какие нет. Это может быть выведено как текстовый файл, как файл изображения или графический вывод (где #FFFFFF - здоровый человек, а # 40FF00 - зараженный человек).

Пожалуйста, можете ли вы включить название языка и команду для его запуска в ответе.

выигрыш

Самый быстрый код для запуска на моем компьютере выигрывает. Его время будет измеряться следующим фрагментом кода Python:

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

Обратите внимание, что при запуске этого скрипта я буду использовать следующие значения по умолчанию:

Probability of transmission = 1
Chance of mutation = 0.01
Number of periods = 1000
Бета распад
источник
3
Вы хотите сделать 10- гигабайтный файл?
Ypnypn
1
Имея ограничение в 4 ГБ, вы полностью удалили опцию сохранения вывода в файл изображения ...
Optimizer
10
1000x1000 : это больше похоже на это!
COTO
1
Также скажите, что есть два человека рядом друг с другом. Первый заражает вирусом V, второй заражает вирусом V'. Сокращение закончится в один и тот же период. Может ли вирус Vзаразить второго человека? (Или более черно-белый вопрос: возможно ли, что человек заразится сразу же после того, как он исцелен, поэтому у него будет 6 последовательных периодов заражения?)
Половина
1
Еще один, два независимых вируса могут мутировать в один и тот же вирус? Скажем, у нас есть Vлично A, и Vснова лично B. Когда они передают вирус, могут ли они мутировать в одну и ту же мутацию V'? Или, может быть, они на самом деле должны мутировать в одном и том же штамме вируса? Если они могут мутировать произвольно, какова вероятность того, что два вируса мутируют в один и тот же штамм вируса?
полугодие

Ответы:

10

Мне было любопытно, как это будет выглядеть, поэтому я быстро и грязно взломал 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()
Андрей
источник
1
Установив для параметра ProtectionProbability значение 1, я получил некоторые из самых приятных шаблонов, которые я когда-либо видел!
Уильям Барбоза
Не могли бы вы добавить, сколько времени занимает ваша программа к вашему ответу?
бета-распад
7

С ++ 11, 6-8 минут

Мой тестовый прогон занимает около 6-8 минут на моей машине Fedora 19, i5. Но из-за случайности мутации, это также может быть быстрее или занять больше времени. Я думаю, что критерии оценки должны быть переадресованы.

Печатает результат в виде текста в конце завершения, здорового человека, обозначенного точкой ( .), зараженного человека звездочкой ( *), если для ANIMATEфлага не установлено значение true, и в этом случае он будет отображать разные символы для людей, зараженных различным штаммом вируса.

Вот GIF для 10x10, 200 периодов.

10x10Gif

Мутационное поведение

Каждая мутация даст новый штамм, который никогда не наблюдался ранее (поэтому возможно, что один человек заразит четырех соседних людей четырьмя различными штаммами), если только не было получено 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;
}
justhalf
источник
Мне действительно это нравится! Мой единственный вопрос, как вы делаете GIF?
бета-распад
1
Я использую этот инструмент: linux.die.net/man/1/byzanz-record . В настоящее время он не имеет графического интерфейса, поэтому вам нужно использовать командную строку = D
justhalf
О, это хорошо, спасибо! :)
Beta Decay
3

C # 6-7 минут

Редактировать 2

Наконец, я (5 часов) сгенерировал подробный вывод в течение почти 1000 периодов (только 840 кадров, после которых произошел сбой) при 1000x1000, каждый 1 период, однако он близок к 160 МБ и требует отображения всей памяти в моей системе (IrfanView) , даже не уверен, что сработает в браузере, может я выложу это позже.

РЕДАКТИРОВАТЬ

Я потратил много времени на то, чтобы сделать это более эффективным, согласно ответу «Бета-распада», в котором говорилось: «Выбирайте штамм случайным образом». У меня есть выбор случайным образом только для выбора того, кто кого заражает за период, однако я изменил рассчитанный метод. и проделал все, я обновил свои сообщения с новыми деталями.

Закодировать мою самую близкую оценку к этому я мог, я надеюсь, что она следует всем правилам, она использует тонну памяти в моей системе (около 1,2 ГБ). Программа может выводить анимированные GIF-файлы (выглядит круто, очень медленно) или просто изображение, соответствующее характеристикам "Beta Decay". Это немного изобретать велосипед, но определенно выглядит круто:


Результаты

(Примечание: это различает только инфицированных и неинфицированных, то есть не многословный)

1000 периодов, 1% мутации, 100% распространения:

Результат

Примеры (Многословный)

В любом случае, использование 100% «вероятности передачи» в не многословном режиме довольно скучно, так как вы всегда получаете одинаковые формы и не видите разных мутаций, если немного подправить параметры (и включить подробный режим) вы получите круто выглядящий результат (анимированные GIF-файлы отображаются в каждом 10-м кадре):

Случайный - размер сетки: 200, вероятности передачи: 100%, вероятности передачи: 1%

100Precent

Случайный - размер сетки: 200, вероятности передачи: 20%, вероятности передачи: 1%

20Precent

счет

Я согласен с «justhalf» в том, что критерии оценки могут быть несправедливыми, поскольку каждый прогон будет отличаться из-за случайности мутаций и положения случайных стартовых точек. Может быть, мы могли бы сделать в среднем несколько прогонов или что-то в этом роде ... ну, в любом случае, это в C #, так что щедрость для меня :( в любом случае.

Код

Обязательно включите библиотеку MagickImage (настроенную на компиляцию 64-битной версии), иначе она не будет собираться ( 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);
        }
    }
}
Дэвид Роджерс
источник