Jadilah Epidemiolog!

13

Tantangan

Anda harus membuat model sederhana tentang bagaimana penyakit menyebar di sekitar sekelompok orang.

Aturan dan Persyaratan

Model harus berupa larik 1000 kali 1000 2D dengan setiap elemen menjadi orang yang berbeda.

Pengguna harus memasukkan tiga variabel menggunakan argv: probabilitas penularan (seberapa besar kemungkinan seseorang akan menginfeksi orang lain), peluang mutasi dan berapa periode simulasi yang harus dijalankan.

Pada periode pertama ( t=0), empat orang harus dipilih secara acak dan terinfeksi penyakit ini.

Cara penyakit berperilaku diatur oleh aturan berikut:

  • Penyakit ini hanya dapat bergerak secara vertikal dan horizontal, pindah ke orang di sebelahnya.
  • Infeksi berlangsung selama 3 periode pada setiap orang. Anda tidak boleh memasukkan faktor imunodefisiensi.
  • Setelah seseorang terinfeksi tiga kali, mereka kebal dan tidak dapat terinfeksi lagi.
  • Penyakit ini tunduk pada mutasi yang membuat orang yang sebelumnya kebal rentan terhadap penyakit mutasi baru ini. Penyakit yang bermutasi memiliki sifat yang persis sama dan mengikuti aturan yang sama dengan penyakit aslinya.
  • Jika terjadi mutasi, seluruh penyakit tidak berubah, hanya 'paket' tertentu pada saat transmisi.
  • Sekali seseorang telah terinfeksi oleh satu virus, mereka tidak dapat terinfeksi lagi sampai infeksi saat ini lewat.
  • Jika seseorang terinfeksi, mereka menular dari awal masa infeksi hingga akhir.
  • Tidak ada tingkat kekebalan - seseorang kebal atau tidak.
  • Untuk menghentikan kelebihan memori, ada batas maksimum 800 mutasi.

Pada akhir jumlah periode yang ditentukan, Anda harus menampilkan hasilnya.

Hasilnya harus berupa kisi 1000 x 1000 yang menunjukkan orang yang terinfeksi dan mana yang tidak. Ini bisa berupa output sebagai file teks, sebagai file gambar atau output grafis (di mana #FFFFFF adalah orang yang sehat dan # 40FF00 adalah orang yang terinfeksi).

Anda dapat memasukkan nama bahasa dan perintah untuk menjalankannya dalam jawaban Anda.

Kemenangan

Kode tercepat untuk dijalankan di komputer saya menang. Waktunya akan diukur dengan potongan kode Python berikut:

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

Perhatikan bahwa saat menjalankan skrip ini, saya akan menggunakan default berikut:

Probability of transmission = 1
Chance of mutation = 0.01
Number of periods = 1000
Peluruhan Beta
sumber
3
Anda ingin membuat file 10- gigabyte ?
Ypnypn
1
Dengan memiliki batas 4 GB, Anda telah sepenuhnya menghapus opsi untuk menyimpan output dalam file gambar ...
Optimizer
10
1000x1000 : Lebih seperti itu!
COTO
1
Juga katakan ada dua orang di sebelah satu sama lain. Virus kontrak pertama V, virus kontrak kedua V'. Kontraksi keduanya akan berakhir pada periode yang sama. Bisakah virus Vmenginfeksi orang kedua? (Atau pertanyaan yang lebih hitam-putih: mungkinkah seseorang terinfeksi segera setelah dia sembuh, sehingga dia akan berakhir dengan 6 periode infeksi berturut-turut?)
justhalf
1
Yang lain, dapatkah dua virus independen bermutasi ke virus yang sama? Katakanlah kita memiliki Vsecara langsung A, dan Vlagi secara langsung B. Ketika mereka menularkan virus, bisakah mereka bermutasi ke mutasi yang sama V'? Atau mungkin mereka sebenarnya harus bermutasi ke strain virus yang sama? Jika mereka dapat bermutasi secara sewenang-wenang, berapakah probabilitas dua virus bermutasi ke jenis virus yang sama?
justhalf

Jawaban:

10

Saya ingin tahu seperti apa ini sehingga saya membuat peretasan yang cepat dan kotor ini dalam 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
sumber
1
Mengatur infeksi. Kemampuan untuk 1 membuat beberapa pola termanis yang pernah saya lihat!
William Barbosa
Bisakah Anda menambahkan berapa lama program Anda menjawab pertanyaan Anda?
Beta Decay
7

C ++ 11, 6-8 menit

Uji coba saya memakan waktu 6-8 menit di mesin Fedora 19, i5 saya. Tetapi karena keacakan mutasi, mungkin lebih cepat atau lebih lama dari itu. Saya pikir kriteria penilaian perlu disusun kembali.

Mencetak hasilnya sebagai teks pada akhir penyelesaian, orang sehat dilambangkan dengan titik (. ), orang yang terinfeksi oleh asterisk ( *), kecuali jika ANIMATEflag disetel ke true, dalam hal ini akan menampilkan karakter yang berbeda untuk orang yang terinfeksi dengan strain virus yang berbeda.

Berikut adalah GIF untuk 10x10, 200 periode.

10x10Gif

Perilaku mutasi

Setiap mutasi akan memberikan galur baru yang tidak pernah terlihat sebelumnya (jadi mungkin satu orang menginfeksi empat orang tetangga dengan 4 galur berbeda), kecuali 800 galur telah dihasilkan, dalam hal ini tidak ada virus yang akan mengalami mutasi lebih lanjut.

Hasil 8 menit berasal dari jumlah orang yang terinfeksi berikut:

Periode 0, Terinfeksi: 4
Periode 100, Terinfeksi: 53743
Periode 200, Terinfeksi: 134451
Periode 300, Terinfeksi: 173369
Periode 400, Terinfeksi: 228176
Periode 500, Terinfeksi: 261473
Periode 600, Terinfeksi: 276086
Periode 700, Terinfeksi: 265774
Periode 800, Terinfeksi: 236828
Periode 900, Terinfeksi: 221275

sedangkan hasil 6 menit berasal dari yang berikut:

Periode 0, Terinfeksi: 4
Periode 100, Terinfeksi: 53627
Periode 200, Terinfeksi: 129033
Periode 300, Terinfeksi: 186127
Periode 400, Terinfeksi: 213633
Periode 500, Terinfeksi: 193702
Periode 600, Terinfeksi: 173995
Periode 700, Terinfeksi: 157966
Periode 800, Terinfeksi: 138281
Periode 900, Terinfeksi: 129381

Representasi orang

Setiap orang diwakili dalam 205 byte. Empat byte untuk menyimpan jenis virus yang dikontrak oleh orang ini, satu byte untuk menyimpan berapa lama orang ini telah terinfeksi, dan 200 byte untuk menyimpan berapa kali ia telah mengontrak setiap jenis virus (masing-masing 2 bit). Mungkin ada beberapa byte-alignment yang dilakukan oleh C ++, tetapi ukuran totalnya sekitar 200MB. Saya memiliki dua kotak untuk menyimpan langkah selanjutnya, jadi totalnya menggunakan sekitar 400MB.

Saya menyimpan lokasi orang yang terinfeksi dalam antrian, untuk memotong waktu yang diperlukan pada periode awal (yang benar-benar berguna hingga periode <400).

Teknis program

Setiap 100 langkah program ini akan mencetak jumlah orang yang terinfeksi, kecuali jika ANIMATEflag ditetapkan true, dalam hal ini akan mencetak seluruh grid setiap 100 ms.

Ini membutuhkan pustaka C ++ 11 (kompilasi menggunakan -std=c++11flag, atau di Mac with clang++ -std=c++11 -stdlib=libc++ virus_spread.cpp -o virus_spread).

Jalankan tanpa argumen untuk nilai default, atau dengan argumen seperti ini:

./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
sumber
Saya sangat menyukai ini! Satu-satunya pertanyaan saya adalah bagaimana Anda membuat GIF?
Beta Decay
1
Saya menggunakan alat ini: linux.die.net/man/1/byzanz-record . Saat ini tidak memiliki GUI, jadi Anda harus menggunakan baris perintah = D
justhalf
Oh, bagus, terima kasih! :)
Beta Decay
3

C # 6-7 Menit

Edit 2

Saya akhirnya (5 jam) menghasilkan output verbose untuk hampir 1000 periode (hanya 840 frame kemudian macet) pada 1000x1000, setiap 1 periode, namun hampir 160MB dan membutuhkan semua memori pada sistem saya untuk ditampilkan (IrfanView) , bahkan tidak yakin itu akan berfungsi di browser, mungkin saya akan memasangnya nanti.

EDIT

Saya telah menghabiskan banyak waktu untuk membuat ini lebih efisien per "Beta Decay" s jawaban yang menyatakan "Pilih strain secara acak" Saya hanya memilih metode acak untuk memilih siapa yang menginfeksi siapa per periode, namun saya telah mengubah cara yang dihitung dan utas segala hal, saya telah memperbarui posting saya dengan detail baru.

Kode estimasi terdekat saya untuk ini saya bisa, saya harap ini mengikuti semua aturan, menggunakan banyak memori pada sistem saya (sekitar 1.2GB). Program ini dapat menampilkan gif animasi (terlihat keren, sangat lambat) atau hanya gambar yang cocok dengan spesifikasi "Peluruhan Beta". Ini sedikit menciptakan kembali roda, tetapi jelas terlihat keren:


Hasil

(Catatan: ini hanya membedakan antara yang terinfeksi dan yang tidak terinfeksi, yaitu non-verbose)

1000 Periode, Tingkat Mutasi 1%, Penyebaran 100%:

Hasil

Contoh (Verbose)

Pokoknya menggunakan 100% "Probabilitas transmisi" dalam mode non-verbose agak membosankan karena Anda selalu mendapatkan bentuk yang sama dan Anda tidak dapat melihat mutasi yang berbeda, jika Anda mengubah parameter sekitar a-bit (dan mengaktifkan mode verbose) Anda mendapatkan beberapa output keren (tampilan GIF animasi setiap frame 10):

Acak - Ukuran Kotak: 200, ProbTransmission: 100%, ProbMutation: 1%

100 sekarang

Acak - Ukuran Kotak: 200, ProbTransmisi: 20%, ProbMutasi: 1%

20 sekarang

Mencetak gol

Saya setuju dengan "justhalf" bahwa kriteria penilaian mungkin tidak adil karena setiap putaran akan berbeda karena keacakan mutasi dan posisi titik awal acak. Mungkin kita bisa melakukan rata-rata beberapa kali berjalan atau semacamnya ..., yah bagaimanapun ini ada di C # jadi hadiah bagi saya :( pokoknya.

Kode

Pastikan untuk menyertakan pustaka MagickImage (atur untuk mengkompilasi bit x64) jika tidak, pustaka itu tidak akan dibuat ( 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
sumber