Sei ein Epidemiologe!

13

Herausforderung

Sie müssen ein einfaches Modell dafür erstellen, wie sich Krankheiten auf eine Gruppe von Menschen ausbreiten.

Regeln und Anforderungen

Das Modell muss ein 1000 x 1000 2D-Array sein, wobei jedes Element eine andere Person ist.

Der Benutzer muss drei Variablen mit argv eingeben: die Übertragungswahrscheinlichkeit (wie wahrscheinlich ist es, dass jemand anderes infiziert), die Wahrscheinlichkeit einer Mutation und die Dauer der Simulation.

In der ersten Periode ( t=0) sollten vier Personen nach dem Zufallsprinzip ausgewählt und mit der Krankheit infiziert werden.

Das Krankheitsverhalten richtet sich nach folgenden Regeln:

  • Die Krankheit kann sich nur vertikal und horizontal bewegen und auf die Person nebenan übertragen.
  • Die Infektion dauert 3 Perioden bei jeder Person. Sie können Immundefekte nicht berücksichtigen.
  • Nachdem eine Person dreimal infiziert wurde, ist sie immun und kann nicht erneut infiziert werden.
  • Die Krankheit unterliegt einer Mutation, die die zuvor immunen Menschen für diese neue mutierte Krankheit anfällig macht. Die mutierte Krankheit hat genau die gleichen Merkmale und folgt den gleichen Regeln wie die ursprüngliche Krankheit.
  • Wenn eine Mutation auftritt, ändert sich die gesamte Krankheit nicht, nur das bestimmte "Paket" bei der Übertragung.
  • Sobald eine Person mit einem Virus infiziert wurde, kann sie erst wieder infiziert werden, wenn die aktuelle Infektion vorüber ist.
  • Wenn eine Person infiziert ist, ist sie vom Beginn ihrer Infektionsperiode bis zu ihrem Ende infektiös.
  • Es gibt keine Stufen der Immunität - eine Person ist entweder immun oder nicht.
  • Um die Speicherüberlastung zu stoppen, sind maximal 800 Mutationen zulässig.

Am Ende der angegebenen Anzahl von Perioden sollten Sie die Ergebnisse ausgeben.

Das Ergebnis muss ein 1000 x 1000-Raster sein, aus dem hervorgeht, welche Personen infiziert sind und welche nicht. Dies kann als Textdatei, als Bilddatei oder als grafische Ausgabe ausgegeben werden (wobei #FFFFFF eine gesunde Person und # 40FF00 eine infizierte Person ist).

Bitte geben Sie in Ihrer Antwort den Namen der Sprache und einen Befehl zum Ausführen an.

Gewinnen

Der schnellste Code, der auf meinem Computer ausgeführt wird, gewinnt. Seine Zeit wird mit dem folgenden Python-Code gemessen:

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

Beachten Sie, dass ich beim Ausführen dieses Skripts die folgenden Standardeinstellungen verwende:

Probability of transmission = 1
Chance of mutation = 0.01
Number of periods = 1000
Beta-Zerfall
quelle
3
Sie möchten eine 10- Gigabyte- Datei erstellen?
Ypnypn
1
Durch die Beschränkung auf 4 GB haben Sie die Option zum Speichern der Ausgabe in einer Bilddatei vollständig aufgehoben ...
Optimizer
10
1000x1000 : Das ist eher so!
COTO
1
Sagen Sie auch, dass zwei Personen nebeneinander sind. Der erste befällt Vden Virus , der zweite befällt den Virus V'. Die Kontraktion endet beide im selben Zeitraum. Kann ein Virus Vdie zweite Person infizieren? (Oder eine eher schwarzweiße Frage:
Ist
1
Kann ein anderer, zwei unabhängige Viren zum gleichen Virus mutieren? Sagen wir, wir haben Vpersönlich Aund Vwieder persönlich B. Können beide, wenn sie das Virus übertragen, zur gleichen Mutation mutieren V'? Oder vielleicht in der Tat sie sollten auf den gleichen Virusstamm mutieren? Wenn sie willkürlich mutieren können, wie hoch ist die Wahrscheinlichkeit, dass zwei Viren zum selben Virusstamm mutieren?
Nur die Hälfte des

Antworten:

10

Ich war gespannt, wie das aussehen würde, also habe ich diesen schnellen und schmutzigen Hack in JavaScript gemacht: 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
quelle
1
Das Setzen von infectionProbability auf 1 hat einige der süßesten Muster hervorgebracht, die ich je gesehen habe!
William Barbosa
Können Sie hinzufügen, wie lange Ihr Programm für Ihre Antwort benötigt?
Beta Decay
7

C ++ 11, 6-8 Minuten

Mein Testlauf dauert auf meiner Fedora 19, i5-Maschine ca. 6-8 Minuten. Aber aufgrund der Zufälligkeit der Mutation kann es genauso gut schneller sein oder länger dauern. Ich denke, dass die Bewertungskriterien neu ausgerichtet werden müssen.

Gibt das Ergebnis am Ende der Fertigstellung als Text aus, gesunde Person mit Punkt ( .), infizierte Person mit Stern ( *), sofern das ANIMATEFlag nicht auf true gesetzt ist. In diesem Fall werden unterschiedliche Zeichen für Personen angezeigt, die mit unterschiedlichen Virusstämmen infiziert sind.

Hier ist ein GIF für 10x10, 200 Perioden.

10x10Gif

Mutationsverhalten

Jede Mutation ergibt einen neuen Stamm, der noch nie zuvor gesehen wurde (es ist also möglich, dass eine Person die vier Nachbarn mit vier verschiedenen Stämmen infiziert), es sei denn, es wurden 800 Stämme erzeugt. In diesem Fall wird kein Virus mehr mutiert.

Das 8-Minuten-Ergebnis stammt von der folgenden Anzahl infizierter Personen:

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

Das 6-Minuten-Ergebnis ergibt sich aus:

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

Personenvertretung

Jede Person ist in 205 Bytes vertreten. Vier Bytes zum Speichern des Virustyps, den diese Person infiziert, ein Byte zum Speichern, wie lange diese Person infiziert war, und 200 Bytes zum Speichern, wie oft sie jeden Virusstamm infiziert hat (jeweils 2 Bit). Möglicherweise werden einige zusätzliche Byte-Alignments von C ++ vorgenommen, aber die Gesamtgröße liegt bei 200 MB. Ich habe zwei Gitter, um den nächsten Schritt zu speichern, also verbraucht es insgesamt ungefähr 400 MB.

Ich speichere den Ort infizierter Personen in einer Warteschlange, um die Zeit zu verkürzen, die in den frühen Perioden benötigt wird (was bis zu Perioden <400 wirklich nützlich ist).

Programmtechniken

Alle 100 Schritte druckt dieses Programm die Anzahl der infizierten Personen, sofern das ANIMATEFlag nicht auf gesetzt trueist. In diesem Fall wird das gesamte Raster alle 100 ms gedruckt.

Dies erfordert C ++ 11-Bibliotheken (Kompilieren mit -std=c++11flag oder in Mac mit clang++ -std=c++11 -stdlib=libc++ virus_spread.cpp -o virus_spread).

Führen Sie es ohne Argumente für die Standardwerte oder mit folgenden Argumenten aus:

./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;
}
nur zur Hälfte
quelle
Das gefällt mir wirklich gut! Meine einzige Frage ist, wie machst du das GIF?
Beta Decay
1
Ich benutze dieses Tool: linux.die.net/man/1/byzanz-record . Derzeit hat es keine GUI, daher müssen Sie die Befehlszeile = D verwenden
halbe
Oh, das ist schön, danke! :)
Beta Decay
3

C # 6-7 Minuten

Bearbeiten 2

Ich habe schließlich (5 Stunden) eine ausführliche Ausgabe für fast 1000 Zeiträume (nur 840 Bilder, dann stürzte sie ab) bei 1000x1000 generiert, alle 1 Zeiträume, jedoch fast 160 MB, und es wird der gesamte Speicher auf meinem System benötigt, um angezeigt zu werden (IrfanView). Ich bin mir nicht einmal sicher, ob das in einem Browser funktionieren würde.

BEARBEITEN

Ich habe viel Zeit darauf verwendet, dies effizienter zu gestalten, je nach der Antwort von "Beta Decay" mit der Aussage "Wähle den Stamm nach dem Zufallsprinzip". Ich habe nur die Zufallsmethode gewählt, um zu bestimmen, wer wen pro Periode infiziert Ich habe meine Posts mit den neuen Details aktualisiert.

Soweit ich das beurteilen kann, hoffe ich, dass alle Regeln eingehalten werden. Es verbraucht eine Tonne Speicher auf meinem System (ungefähr 1,2 GB). Das Programm kann animierte GIFs (sieht cool aus, sehr langsam) oder nur ein Bild ausgeben, das den Spezifikationen von "Beta Decay" entspricht. Das ist ein bisschen, um das Rad neu zu erfinden, sieht aber auf jeden Fall cool aus:


Ergebnisse

(Hinweis: Dies unterscheidet nur zwischen infiziert und nicht infiziert, dh nicht ausführlich)

1000 Perioden, 1% Mutationsrate, 100% Verbreitung:

Ergebnis

Beispiele (ausführlich)

Auf jeden Fall ist die Verwendung von 100% "Übertragungswahrscheinlichkeit" im nicht-ausführlichen Modus etwas langweilig, da Sie immer die gleichen Formen erhalten und die verschiedenen Mutationen nicht sehen können, wenn Sie die Parameter ein wenig verändern (und den ausführlichen Modus aktivieren). Sie erhalten eine cool aussehende Ausgabe (animierte GIFs werden alle 10 Bilder angezeigt):

Zufällig - Rastergröße: 200, ProbTransmission: 100%, ProbMutation: 1%

100 Prozent

Zufällig - Rastergröße: 200, ProbTransmission: 20%, ProbMutation: 1%

20 Prozent

Wertung

Ich stimme "justhalf" zu, dass die Bewertungskriterien möglicherweise nicht fair sind, da sich jeder Lauf aufgrund der Zufälligkeit der Mutationen und der Position der zufälligen Startpunkte unterscheidet. Vielleicht könnten wir durchschnittlich mehrere Läufe machen oder so ... naja, trotzdem ist das in C #, also Kopfgeld für mich :( trotzdem.

Code

Stellen Sie sicher, dass Sie die MagickImage-Bibliothek einschließen (auf Kompilieren von x64-Bit eingestellt), da sie sonst nicht erstellt werden kann ( 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
quelle