Março de 2016

Volume 31 Número 3

C# - Simulação discreta de evento: Um exemplo de crescimento demográfico

Por Arnaldo Perez Perez

Ao longo da história, a capacidade de simular auxiliou o desenvolvimento de várias ciências. Os modelos médicos que simulam o corpo humano aprimoram o estudo da anatomia humana. Os jogos de simulação de computador como o “World of Warcraft” recriam um mundo de fantasia inteiro e o “Simulador de voo” ajuda a treinar os pilotos em solo. Diversos programas de simulação exploram as respostas a ataques terroristas, a doenças pandêmicas e a outras crises possíveis. Até mesmo os dinossauros simulados no filme “Jurassic Park” dão uma dica sobre a ampla aplicação de simulação e sobre seu potencial.

A simulação é uma técnica na qual um sistema ou um processo real é emulado por um modelo projetado. O modelo encapsula todos os recursos e os comportamentos do sistema; a simulação é a execução desse sistema ao longo do tempo. Existem diversos estágios para o design de uma simulação:

  • A definição do sistema a ser modelado, que envolve o estudo do problema existente, a identificação das propriedades do ambiente e a especificação dos objetivos a serem atingidos.
  • A formulação do modelo, que inclui a definição de todas as suas variáveis e de suas relações lógicas e a criação dos fluxogramas necessários.
  • A definição dos dados exigidos pelo modelo para a produção do resultado desejado.
  • A produção de uma implementação computadorizada do modelo.
  • A verificação se o modelo implementado satisfaz o design.
  • A validação por meio de comparação de que o simulador realmente representa o sistema real simulado.
  • A experimentação para gerar os dados desejados usando o simulador.
  • A análise e a interpretação dos resultados do simulador e a tomada de decisões com base nesses resultados.
  • A documentação do modelo criado e o simulador como uma ferramenta.

Geralmente, as simulações compõem um processo contínuo ou eventos discretos. Para simular um sistema meteorológico, por exemplo, o controle ocorre continuamente, já que todos os elementos estão em constante mudança. Consequentemente, a variável de temperatura em relação à variável tempo seria representada por uma curva contínua. Em contraste, as decolagens e os pousos de aeronaves ocorrem como pontos no tempo e, portanto, uma simulação só poderá considerar esses momentos ou eventos precisos e descartar todo o resto. Esse tipo de simulação é conhecido como uma DES (simulação discreta de evento) e é o que discutiremos neste artigo.

Simulação discreta de evento

A DES modela um sistema ou processo como uma sequência ordenada de eventos individuais com o tempo, ou seja, do horário de um evento até o horário do próximo evento. Consequentemente, em uma simulação DES, o tempo costuma ser muito mais curto do que o tempo real.

Ao desenvolver uma DES, há seis elementos principais a serem considerados:

Os objetos representam elementos do sistema real. Eles têm propriedades, eles estão relacionados a eventos, eles consumem recursos e eles entram e saem de filas ao longo do tempo. No cenário de pouso e de decolagem de aeronaves mencionado anteriormente, os objetos seriam as aeronaves. Em um sistema de Assistência Médica, os objetos podem ser pacientes ou órgãos. Em um sistema de depósito, os objetos seriam os produtos em estoque. Os objetos devem interagir uns com os outros ou com o sistema, e podem ser criados a qualquer momento durante uma simulação.

As propriedades são os recursos particulares de cada objeto (tamanho, hora do pouso, sexo, preço e assim por diante) armazenado para determinar as respostas a diversos cenários que possam ocorrer na simulação; tais valores podem ser modificados.

Os eventos são o que pode ocorrer no sistema, especialmente aos objetos, como o pouso de uma aeronave, a chegada de um produto em um depósito, o aspecto de uma determinada doença e assim por diante. Os eventos podem ocorrer e ocorrer novamente em qualquer ordem.

Os recursos são os elementos que fornecem serviços a objetos (por exemplo, uma pista de pouso em um aeroporto, células de armazenamento em um depósito e médicos em uma clínica). Quando um recurso estiver ocupado e se um objeto precisar dele, o objeto deverá entrar na fila e aguardar até que o recurso esteja disponível.

As filas são os condutores nos quais os objetos são organizados para aguardar a liberação de um recurso ocupado no momento. As filas podem ter uma capacidade máxima e podem ter diferentes abordagens de chamada: FIFO (primeiro a entrar, primeiro a sair), LIFO (último a entrar, primeiro a sair) ou com base em alguns critérios ou prioridades (progressão da doença, consumo de combustível, entre outros).

O tempo (como acontece na vida real) é essencial na simulação. Para medir o tempo, um relógio é iniciado no início de uma simulação e então pode ser usado para controlar períodos em particular (hora da decolagem ou do pouso, tempo de transporte, tempo gasto com determinados sintomas e assim por diante). Esse controle é fundamental porque permite que você saiba quando o próximo evento deverá ocorrer.

Como a programação da simulação pode ser complicada, têm havido diversas tentativas de criar linguagens que incorporem todos os requisitos do paradigma de simulação para facilitar o desenvolvimento. Uma dessas linguagens é a SIMULA, inventada nos anos 60 por Ole Johan e Kristen Nygaard e a primeira a introduzir o conceito de programação orientada a objeto (OOP), o paradigma de programação mais utilizado hoje. Hoje, o foco se concentra na criação de pacotes, de estruturas ou de bibliotecas que incorporem a necessidade do programador ao criar uma simulação. Essas bibliotecas devem ser chamadas de linguagens comuns, como C#, C++, Java ou Python.

Em “A History of Discrete Event Simulation Programming Languages,” Nance propôs seis requisitos mínimos que qualquer linguagem de programação DES deveria satisfazer (bit.ly/1ZnvkVn):

  • A geração de números aleatórios.
  • Os transformadores de processos, para permitir variáveis diferentes das variáveis aleatórias uniformes.
  • O processamento de listas, para facilitar a criação, a manipulação e a exclusão de objetos.
  • A análise estatística, para oferecer um resumo descritivo do comportamento do modelo.
  • A geração de relatórios, para auxiliar na apresentação de grandes conjuntos de dados e para facilitar a tomada de decisão.
  • Um mecanismo de fluxo de tempo.

Todos esses requisitos podem ser satisfeitos no C# e, portanto, apresentarei neste artigo uma simulação discreta de evento para o crescimento demográfico desenvolvida em C#.

DES para crescimento demográfico

O crescimento demográfico é um dos vários aspectos considerados no estudo de como as populações (animais e plantas) mudam ao longo do tempo e do espaço e interagem com seu ambiente. As populações são grupos de organismos da mesma espécie vivendo ao mesmo tempo, algumas vezes no mesmo espaço ou ainda mais delimitado por determinadas características.

Por que é importante estudar o crescimento demográfico? Uma melhor compreensão de como as populações crescem ou encolhem dá aos cientistas a possibilidade de fazer previsões melhores sobre as mudanças na conservação da biodiversidade, o uso de recursos, a mudança climática, a poluição, a assistência médica, as necessidades de trasporte e assim por diante. Ele também oferece informações sobre como os organismos interagem uns com os outros e com o ambiente, um aspecto fundamental quando consideramos se uma população poderá prosperar ou declinar.

Neste artigo, apresentarei uma DES para o crescimento de uma população. O objetivo é observar como a população evolui com o tempo e obter algumas estatísticas da análise dos resultados finais (tamanho da população, idosos, crianças e assim por diante). A população começará com m indivíduos masculinos e n indivíduos femininos, cada um com uma idade associada. Claramente, me e n devem ser maiores do que zero ou a simulação não fará sentido. Os objetos deste modelo são os indivíduos.

Um indivíduo é capaz de iniciar um relacionamento com outro indivíduo depois de atingir uma idade que distribua uma função Poisson com o parâmetro λ = 18 anos. (No momento, você não precisa compreender totalmente as distribuições normais ou exponenciais ou Poisson; eles serão explicados na próxima seção).

Há uma probabilidade de menos de 50% de que indivíduos de sexos opostos solteiros e capazes se relacionem uns com os outros, e mesmo isso só ocorrerá quando a diferença de idade não for maior do que cinco anos. A Figura 1 mostra um exemplo da probabilidade do término de um relacionamento entre dois indivíduos.

Figura 1 Probabilidade do término de um relacionamento

Idade média Probabilidade
14 a 20 0,7
21 a 28 0,5
Mais de 29 0,2

Os indivíduos envolvidos em um relacionamento podem ter um filho depois de um tempo, distribuído por uma função exponencial com o parâmetro λ= 8 anos.

Uma mulher pode ficar grávida se estiver em uma idade que siga uma função de distribuição normal (em forma de sino) com os parâmetros µ = 28 e σ2= 8 anos. Todas as mulheres têm um número de filhos distribuído por uma função normal com os parâmetros µ = 2 e σ2= 6 anos. (O parâmetro µ representa a idade média, enquanto σ2é a medida da variabilidade da idade).

Todos os indivíduos têm uma expectativa de vida distribuída por uma função Poisson com o λ= 70 anos, onde λ representa a expectativa de vida média.

Na descrição anterior, é possível identificar vários eventos:

  • Iniciar um relacionamento.
  • Terminar um relacionamento.
  • Ficar grávida.
  • Ter um bebê.
  • Morrer.

Cada evento é acompanhado por uma variável aleatória discreta que determina o momento no qual cada evento ocorrerá.

Distribuições probabilísticas

O conjunto de uma variável aleatória discreta é finito e contavelmente infinito. Ou seja, os valores podem ser listados como uma sequência finita ou infinita de valores 1, 2, 3... Para uma variável aleatória discreta, sua distribuição de probabilidade é qualquer gráfico, tabela ou fórmula que atribua uma probabilidade a cada valor possível. A soma de todas as probabilidades deve ser 1, e cada probabilidade individual deve estar entre 0 e 1. Por exemplo, ao lançar um dado não viciado (todos os lados igualmente prováveis), a variável aleatória discreta X que representa os resultados possíveis terá a distribuição probabilística X(1) = 1/6, X(2) = 1/6, …, X(6) = 1/6. Todos os lados são igualmente prováveis e, portanto, a probabilidade de cada um deles é 1/6.

Os parâmetros l e µ indicam a média (valor esperado) e as distribuições correspondentes. A média representa o valor que a variável aleatória assume na média. Em outras palavras, é a soma E=[(cada resultado possível) × (probabilidade desse resultado)], onde E denota a média. No caso do dado, a média seria E = 1/6 + 2/6 + 3/6 + 4/6 + 5/6 + 6/6 = 3,5. Observe que o resultado 3,5 está, na verdade, na metade do caminho entre todos os valores possíveis que o dado pode mostrar; é o valor esperado quando o dado é lançado um grande número de vezes.

O parâmetro σ2 indica a variação da distribuição. A variação representa a dispersão de valores possíveis da variável aleatória e nunca será negativa. As pequenas variações (próximas a 0) tendem a indicar que os valores estão próximos uns dos outros e da média; as variações altas (próximas a 1) indicam uma longa distância entre os valores e da média.

Poisson é uma distribuição discreta que expressa as probabilidades em relação ao número de eventos por unidade de tempo (veja a Figura 2). Normalmente é aplicada quando a probabilidade de um evento é pequena e o número de oportunidades para que ele ocorra é muito grande. O número de erros de impressão em um livro, os clientes chegando a um centro comercial, os carros chegando a um sinal de trânsito e as mortes por ano em uma determinada faixa etária são exemplos de aplicações da distribuição Poisson.

Distribuição Poisson, Parâmetro λ = 18
Figura 2 Distribuição Poisson, Parâmetro λ = 18

Uma distribuição exponencial expressa o tempo entre os eventos em um processo Poisson (veja a Figura 3). Por exemplo, se você estiver lidando com um processo Poisson que descreve o número de clientes que chegam a um centro comercial durante um tempo determinado, talvez esteja interessado em uma variável aleatória que indicaria quanto tempo se passou antes da chegada do primeiro cliente. Uma distribuição exponencial pode servir a essa finalidade. Também poderia ser aplicada a processos da física, por exemplo para representar o tempo de vida das partículas, onde λ indica a taxa na qual a partícula envelhece.

Distribuição Exponencial, Parâmetro λ = 8
Figura 3 Distribuição Exponencial, Parâmetro λ = 8

A distribuição normal descreve as probabilidades que tendem a recair em torno de um valor central, sem desvio para a direita ou para esquerda, como mostrado na Figura 4. As distribuições normais são simétricas e possuem curvas de densidade em formato de sino com um único pico na média. 50% da distribuição recai à esquerda da média e 50% à direita. O desvio padrão indica a disseminação ou o perímetro da curva de sino; quanto menor o desvio padrão, mais concentrados serão os dados. A média e o desvio padrão devem ser indicados como parâmetros para a distribuição normal. Muitos fenômenos naturais seguem de perto uma distribuição normal: pressão sanguínea, altura das pessoas, erros nas medidas e muitos outros.

NormalDistribution, parâmetros µ = 28 e σ2 = 8 Anos
Figura 4 NormalDistribution, parâmetros µ = 28 e σ2 = 8 Anos

Agora mostrarei como implementar a DES proposta em uma linguagem popular, sofisticada e elegante, como o C#.

 

Implementação

Para desenvolver esta simulação, vou explorar todos os benefícios do paradigma OOP. A ideia é obter o código mais legível possível. Os aplicativos científicos tendem a ser mais complexos e difíceis de compreender e é essencial tentar fazer com que fiquem os mais claros possíveis, de forma que as outras pessoas possam compreender seu código. Desenvolverei o aplicativo como um aplicativo de console em C#, com a estrutura mostrada na Figura 5.

A estrutura do aplicativo de simulação
Figura 5 A estrutura do aplicativo de simulação

Um caminho lógico é essencial; observe como a estrutura do namespace mantém seu próximo sendo comum: Simulation.DES.PopulationGrowth.

A pasta Events contém um arquivo Event.cs, que define um tipo enum que representa todos os eventos possíveis na emulação:

namespace DES.PopulationGrowth.Events
{
public enum Event
  {
Capable Engaging,
Birth EngageDisengage,
GetPregnant,
ChildrenCount,
TimeChildren,
    Die
  }
}

A pasta Objects contém todas as classes relacionadas aos indivíduos; são as partes do código que se beneficiarão mais do OOP. Existem três classes relacionadas aos indivíduos: Individual, Male e Female. A primeira é uma classe abstrata e as outras herdam dela; ou seja, machos e fêmeas são indivíduos. A maior parte da codificação ocorre na classe Individual. A Figura 6 mostra suas propriedades e seus métodos.

Propriedades e métodos da classe Individual
Figura 6 Propriedades e métodos da classe Individual

Veja uma descrição de cada propriedade:

  • Age: Idade do indivíduo.
  • Couple: Nulo se o indivíduo for solteiro; caso contrário, seu cônjuge, outro Individual.
  • Engaged: Verdadeiro se o indivíduo estiver em um relacionamento; caso contrário, falso.
  • LifeTime: Idade em que o indivíduo morre.
  • RelationAge: Idade na qual o indivíduo pode iniciar um relacionamento.
  • TimeChildren: Tempo na simulação (não é a idade) na qual o indivíduo pode ter filhos.

E veja uma descrição dos métodos:

  • Disengage: Termina o relacionamento entre dois indivíduos.
  • EndRelation: Determina se o relacionamento deve terminar, de acordo com a tabela de probabilidades da Figura 1.
  • FindPartner: Encontra um parceiro disponível para um indivíduo.
  • SuitablePartner: Determina se um indivíduo é adequado para iniciar um relacionamento (diferença de idade e sexo oposto).
  • SuitableRelation: Determina se algum indivíduo pode iniciar um relacionamento; ou seja, é solteiro e está na idade para iniciar um relacionamento.
  • ToString: Substituição para representar informações do indivíduo como uma cadeia de caracteres.

A classe começa ao definir todas as propriedades e depois o construtor, que simplesmente define a idade:

public abstract class Individual
  {
public int Age { get; set; }
public int RelationAge{ get; set; }
public int LifeTime{ get; set; }
public double TimeChildren{ get; set; }
public Individual Couple { get; set; }
protected Individual(int age)
{
  Age = age;
}

Os métodos SuitableRelation e SuitablePartner e a propriedade Engaged são apenas testes lógicos, bastante diretos:

public bool SuitableRelation()
{
return Age >= RelationAge&& Couple == null;
}
public bool SuitablePartner(Individual individual)
{
return ((individual is Male && this is Female) ||
(individual is Female && this is Male)) &&Math.Abs(individual.Age - Age) <= 5;
}  
public bool Engaged
{
get { return Couple != null; }
}

O método Disengage divide um relacionamento ao definir o campo Couple como nulo no casal e posteriormente no indivíduo. Ele também define o tempo para ter filhos como 0 porque eles não estão mais comprometidos.

public void Disengage()
{
Couple.Couple = null;
Couple = null;
TimeChildren = 0;
}

O método EndRelation é basicamente uma tradução da tabela de probabilidade para a determinação da chance de um relacionamento terminar. Ele usa uma variável aleatória uniforme, que produz um valor aleatório no intervalo [0,1] equivalente à produção de uma porcentagem aceitável. O dicionário de distribuições é criado na classe de simulação (descrita em breve) e contém pares (evento, distribuição de probabilidade), associando cada evento à sua distribuição:

public bool EndRelation(Dictionary<Event, IDistribution> distributions)
{
var sample =
  ((ContinuousUniform) distributions[Event.BirthEngageDisengage]).Sample();
if (Age >= 14 && Age <= 20 && sample <= 0.7)
return true;
if (Age >= 21 && Age <= 28 && sample <= 0.5)
return true;
if (Age >= 29 && sample <= 0.2)
return true;
return false;
}

O método FindPartner, mostrado na Figura 7, localizada um parceiro disponível para a instância individual. Ele recebe como entrada a lista da população, a hora atual da simulação e o dicionário de distribuições.

Figura 7 O método FindPartner

public void FindPartner(IEnumerable<Individual> population, int currentTime,
  Dictionary<Event, IDistribution> distributions)
{
foreach (var candidate in population)
if (SuitablePartner(candidate) &&
candidate.SuitableRelation() &&
((ContinuousUniform) distributions[Event.BirthEngageDisengage]).Sample() <= 0.5)
{
// Relate them
candidate.Couple = this;
Couple = candidate;
// Set time for having child
var childTime = ((Exponential)  distributions[Event.TimeChildren]).Sample()*100;
// They can have children on the simulated year: 'currentTime + childTime'.
candidate.TimeChildren = currentTime + childTime;
TimeChildren = currentTime + childTime;
break;
}
}

Por fim, o método ToString define a representação de cadeia de caracteres de um indivíduo:

public override string ToString()
{
Return string.Format("Age: {0} Lifetime {1}", Age, LifeTime);
}

A classe Male é muito simples:

public class Male: Individual
{
public Male(int age) : base(age)
{
}
public override string ToString()
{
return base.ToString() + " Male";
}
}

A classe Female é um pouco mais complicada porque inclui tratamento para gravidez, nascimento e assim por diante. A definição da classe começa ao declarar todas as propriedades e o construtor:

public class Female :Individual
{
public bool IsPregnant{ get; set; }
public double PregnancyAge{ get; set; }
public double ChildrenCount{ get; set; }
public Female(int age) : base(age)
{
}
}

Veja as propriedades da classe Female:

  • IsPregnant: Determina se a mulher está grávida.
  • PregnancyAge: Determina a idade em que a mulher pode ficar grávida.
  • ChildrenCount: Indica o número de filhos que devem nascer.

E estes são os métodos que ele contém:

  • SuitablePregnancy: Determina se a mulher pode ficar grávida.
  • GiveBirth: Indica uma mulher dando à luz, adicionando um novo indivíduo à população.
  • ToString:override: Usado para representar informações sobre a fêmea como uma cadeia de caracteres.

SuitablePregnancy é um método de teste que tem como saída verdadeiro quando a instância mulher atende a todas as condições para ficar grávida:

public bool SuitablePregnancy(intcurrentTime)
  {
return Age >= PregnancyAge && currentTime <= TimeChildren && ChildrenCount > 0;
}

O método GiveBirth, mostrado na Figura 8, é o código que adiciona e inicializa novos indivíduos na população.

Figura 8 O método GiveBirth

public Individual GiveBirth(Dictionary<Event, IDistribution> distributions, int currentTime)
  {
var sample =
  ((ContinuousUniform) distributions[Event.BirthEngageDisengage]).Sample();
var child = sample > 0.5 ? (Individual) newMale(0) : newFemale(0);
// One less child to give birth to
ChildrenCount--;
child.LifeTime = ((Poisson)distributions[Event.Die]).Sample();
child.RelationAge = ((Poisson)distributions[Event.CapableEngaging]).Sample();
if (child is Female)
{
(child as Female).PregnancyAge =
  ((Normal)distributions[Event.GetPregnant]).Sample();
(child as Female).ChildrenCount =
  ((Normal)distributions[Event.ChildrenCount]).Sample();
}
if (Engaged && ChildrenCount> 0)
{
TimeChildren =
  currentTime + ((Exponential)distributions[Event.TimeChildren]).Sample();
Couple.TimeChildren = TimeChildren;
}
else
TimeChildren = 0;
IsPregnant = false;
return child;
  }

Um exemplo uniforme é gerado primeiro para determinar se o novo indivíduo será macho ou fêmea, cada um com uma probabilidade de 50% (0,5). O valor de ChildrenCount é decrementado em 1, indicando que essa mulher tem um filho a menos para dar à luz. O resto do código está relacionado à inicialização individual e à redefinição de algumas variáveis.

O método ToString altera a maneira na qual Females são representadas como cadeias de caracteres:

public override string ToString()
  {
return base.ToString() + " Female";
}

Como todas as funções relacionadas aos indivíduos foram posicionadas em classes separadas, a classe Simulation agora é muito mais simples. As propriedades e o construtor estão no início do código:

public class Simulation
  {
public List<Individual> Population { get; set; }
public int Time { get; set; }
private int _currentTime;
private readonly Dictionary<Event, IDistribution> _distributions ;

As propriedades e as variáveis são auto-descritivas e algumas foram descritas anteriormente. Time representa o tempo (em anos) de duração da simulação e _currentTime representa o ano atual da simulação. O construtor deste caso, mostrado na Figura 9, é mais complicado porque inclui a inicialização de variáveis aleatórias para cada indivíduo.

Figura 9 O construtor da classe Simulation

public Simulation(IEnumerable<Individual> population, int time)
{
Population = new List<Individual>(population);
Time = time;
_distributions = new Dictionary<Event, IDistribution>
{
{ Event.CapableEngaging, new Poisson(18) },
{ Event.BirthEngageDisengage, new ContinuousUniform() },
{ Event.GetPregnant, new Normal(28, 8) },
{ Event.ChildrenCount, new Normal(2, 6) },
{ Event.TimeChildren, new Exponential(8) },
{ Event.Die, new Poisson(70) },
                                 };
foreach (var individual in Population)
{
// LifeTime
individual.LifeTime = ((Poisson) _distributions[Event.Die]).Sample();
// Ready to start having relations
individual.RelationAge = ((Poisson)_distributions[Event.CapableEngaging]).Sample();
// Pregnancy Age (only women)
if (individual is Female)
                {
(individual as Female).PregnancyAge = ((Normal) _distributions[Event.GetPregnant]).Sample();
(individual as Female).ChildrenCount = ((Normal)_distributions[Event.ChildrenCount]).Sample();
}
}
}

Por fim, o método Execute, mostrado na Figura 10, é onde ocorre toda a lógica de simulação.

Figura 10 O método Execute

public void Execute()
{
while (_currentTime< Time)
{
// Check what happens to every individual this year
for (vari = 0; i<Population.Count; i++)
{
var individual = Population[i];
// Event -> Birth
if (individual is Female&& (individual as Female).IsPregnant)
Population.Add((individual as Female).GiveBirth(_distributions,
  _currentTime));
// Event -> Check whether someone starts a relationship this year
if (individual.SuitableRelation())
individual.FindPartner(Population, _currentTime, _distributions);
// Events where having an engaged individual represents a prerequisite
if (individual.Engaged)
{
// Event -> Check whether arelationship ends this year
if (individual.EndRelation(_distributions))
individual.Disengage();
// Event -> Check whether a couple can have a child now
if (individual is Female &&
(individual as Female).SuitablePregnancy(_currentTime))
(individual as Female).IsPregnant = true;
}
// Event -> Check whether someone dies this year
if (individual.Age.Equals(individual.LifeTime))
{
// Case: Individual in relationship (break relation)
if (individual.Engaged)
individual.Disengage();
Population.RemoveAt(i);
}
individual.Age++;
_currentTime++;
}
}

As iterações no loop externo representam anos passados na simulação. O loop interno passa por eventos que podem ocorrer aos indivíduos naquele ano em particular.

Para ver como a população evolui com o tempo, configuro um novo aplicativo de console, mostrado na Figura 11.

Figura 11 Exibição da população ao longo do tempo

static void main()
{
var population = new List<Individual>
{
new Male(2),
new Female(2),
new Male(3),
new Female(4),
new Male(5),
new Female(3)
};
var sim = new Simulation(population, 1000);
sim.Execute();
// Print population after simulation
foreach (var individual in sim.Population)
Console.WriteLine("Individual {0}", individual);
Console.ReadLine();
}

A Figura 12 mostra a população depois de 1.000 anos.

População após 1.000 anos
Figura 12 População após 1.000 anos

Neste artigo, desenvolvi uma simulação discreta de evento para ver como a população evolui no tempo. A abordagem orientada a objeto se provou bastante útil para a obtenção de código legível e conciso que os leitores poderão experimentar e aprimorar, se necessário.


Arnaldo Pérez Castaño* é um cientista da computação que mora em Cuba. Ele é autor de uma série de livros de programação — “JavaScript Fácil,” “HTML y CSS Fácil” e “Python Fácil” (Marcombo S.A). Sua experiência inclui Visual Basic, C#, .NET Framework e Inteligência Artificial, e oferece seus serviços como freelancer por meio do site nubelo.com. Cinema e música são algumas de suas paixões. Entre em contato com ele pelo email <arnaldo.skywalker@gmail.com.>*

Agradecemos ao seguinte especialista técnico da Microsoft pela revisão deste artigo: James McCaffrey