Mostrando postagens com marcador Probabilidade. Mostrar todas as postagens
Mostrando postagens com marcador Probabilidade. Mostrar todas as postagens

segunda-feira, 24 de setembro de 2012

Cadeia de Markov


A cadeia de Markov é ferramenta que modela uma grande variedade de situações recorrentes em biologia, economia, química, engenharia e física envolvendo processos estocásticos . Nesses experimentos, a predição do estado seguinte depende apenas do estado atual. Esta propriedade é chamada de memória markoviana (ou falta de memória).

Considere a seguinte distribuição de probabilidade de uma população na zona urbana e zona rural:  

vo = [0.6 0.4]

Este vetor indica que 60% da população vive na zona urbana e 40% da população vive na zona rural.

O próximo estado pode ser encontrado aplicando uma matriz de transição  (matriz estocástica) é uma matriz quadrada com duas características:
1) todas as entradas são não negativas
2) todas as colunas tem soma igual a 1.

Por ano, 5% da população sai da zona urbana para a zona rural e 3% da população sai da zona rural para a zona urbana.

  sai urbana   rural
P = [ .95      0.03]urbana
      [ .05      0.97]rural
                           chega  


Podemos calcular a nova distribuição da população aplicando a matriz de transição:

Pvo = [0.95 0.03][0.6] = [0.582]
          [0.05 0.97][0.4]    [0.418]


A cadeia de Markov é a sequência de vetores de distribuição de probabilidade v0,v1,v2,..., com uma matriz de transição da seguinte maneira:

$v_{1} = Pv_0$
$v_2 = Pv_1$
$v_3 = Pv_2$
...
$v_n = Pv_{n-1}$

$v_n = P^{n} v_0$

Matriz de transição estacionária

A cadeia de Markov pode ser utilizada para encontrar . A matriz $P^{k}$ é a matriz de transição em k passos. A matriz $P^{k}$ converge para uma matriz estacionária. No nosso algoritmo, vamos considerar
um valor de k suficientemente grande para obter a matriz estacionária.

Primeiramente, temos a função que aloca uma matriz quadrada dinamicamente (alocam), a função multiplica que faz a multiplicação entre duas matrizes (a e b) devolvendo uma matriz c e a função que executa a exponenciação rápida de uma matriz $A^{n}$

double ** alocam(int n){
 int i;
 double ** p = (double **)malloc(n*sizeof(double *));
 
 for(i=0;i<n;i++)
  p[i] = (double *) malloc(n*sizeof(double));
 return p;
}

void multiplica(double  **a, double  **b, double  **c, int n){
int i,j,k;
 for(i=0;i<n;i++){
  for(j=0;j<n;j++){
 c[i][j] = 0.0;     
 for(k=0;k<n;k++){
   c[i][j] += a[i][k]*b[k][j];
 }
  }
 }
}
void exp(double **a, double **b,int n, int m){
  int i,j;
  double ** c = alocam(n);
  if(m==0){
    for(i=0;i<n;i++)
      for(j=0;j<n;j++)
        if(i==j) b[i][j] = 1.0;
        else b[i][j] = 0.0;
  }else if(m==1){
 for(i=0;i<n;i++)
   for(j=0;j<n;j++)
     b[i][j] = a[i][j];
   }else if(m%2==0){
 exp(a,c,n,m/2);
 multiplica(c,c,b,n);
   }else if(m%2==1){
 exp(a,c,n,m-1);
 multiplica(c,a,b,n);
   }
   free(c);
}
Agora, precisamos apenas inicializar a matriz de transição e depois calcular $A^n$ para n suficientemente grande.

int main(){
 int n,s,b,c;
 int i,j,k; 
 double *v0;
 double *vf;

 m = alocam(2);
 r = alocam(2);
 v0 = aloca(2);
 vf = aloca(2);
 
 m[0][0] = 0.95;
 m[0][1] = 0.03;
 m[1][0] = 0.05;
 m[1][1] = 0.97;
 
 v0[0] = 0.4;
 v0[1] = 0.6;
 
 printf("Matriz Inicial\n"); 
 for(i=0;i<2;i++){
  for(j=0;j<2;j++){
   printf("%lf ",m[i][j]);
  }
  printf("\n");
 }
 printf("\n");
 exp(m,r,2,10000); 
 
 printf("Matriz Estacionaria\n");
 for(i=0;i<2;i++){
  for(j=0;j<2;j++){
   printf("%lf ",r[i][j]);
  }
  printf("\n");
 }
 printf("\n");
 free(m);
 free(r);
}

Matriz Inicial
0.950000 0.030000
0.050000 0.970000

Matriz Estacionaria
0.375000 0.375000
0.625000 0.625000


Podemos concluir que a longo prazo, a população urbana será 37,5% e a população rural será 62,5% considerando a manutenção dessas taxas.


Outra maneira de encontrar o estado estacionário da cadeia de Markov é resolver a seguinte equação:

Px = x
Px-x=0
Px-Ix=0
(P-I)x=0


Você pode resolver os seguintes problemas com essa ideia:
http://br.spoj.pl/problems/VAMPIROS/
http://www.urionlinejudge.com.br/urionlinejudge/problems/view/1227

sexta-feira, 14 de setembro de 2012

Probabilidade - Problemas clássicos em probabilidade



Um dos problemas clássicos em probabilidade foi proposto por Pacioli em sua famosa Summa. Este problema ficou conhecido como o problema da divisão dos pontos:

Dois jogadores disputavam um prêmio de 64 moedas que seria dado a quem primeiro fizesse 3 pontos no jogo. Quando o primeiro jogador tinha 2 pontos e o segundo tinha 1 pontos, foi preciso interromper o jogo. Como dividir o prêmio ?

A solução deste problema exigia o desenvolvimento de ferramenta matemática capaz de analisar todas as possibilidades futuras do jogo para calcular corretamente a probabilidade de vitória de cada jogador. Essa ferramenta ficou conhecida como esperança matemática.

A solução dada por Pascal é o produto do ganho eventual pela probabilidade desse ganho. Se o primeiro jogador ganhar a próxima partida, então ele venceria o jogo. Logo, podemos dizer que ele tem direito a metade das moedas (neste caso, 32 moedas) se isso acontecer. Se o primeiro jogador não ganhar a próxima partida, então cada jogador ficará com 2 pontos. O que pode acontecer agora? Se o primeiro jogador vence então ele tem o direito a metade da metade das moedas (neste caso, 16 moedas);caso contrário, o segundo jogador recebe 16 moedas. A divisão baseada na probabilidade de vitória de cada jogador seria:
·        O jogador 1 recebe 48 moedas.
·        O jogador 2 recebe 16 moedas.

Podemos perceber que este problema tem a seguinte estrutura recursiva:
P[i,j] = probabilidade do jogador 1 vencer sabendo que o jogador 1 tem i pontos e o jogador 2 tem j pontos e o primeiro jogador que chegar em n pontos vence a partida.
P[n,j] = 1.0 , j=0..2
P[i,n] =0.0 ,  i=0..2
P[i,j] = 0.5*P[i+1,j] +0.5*P[i,j+1] , i!=n e j!=n



double prob(int j1, int j2){
 if( j1 == n ) return 1.0;
 if( j2 == n ) return 0.0;
 return 0.5*prob(j1+1,j2) + 0.5*prob(j1,j2+1);
}

numero de vitorias do jogador 1:2
numero de vitorias do jogador 2:1
numero de jogos:3
0.750000

numero de vitorias do jogador 1:5
numero de vitorias do jogador 2:3
numero de jogos:7
0.812500

Podemos utilizar a memorização para evitar cálculos desnecessários.
double prob2(int i, int j){
 if( i == n ) return 1.0;
 if( j == n ) return 0.0;
 if( P[i][j] > 0 ) return P[i][j];
 P[i][j] = 0.5*prob2(i+1,j) + 0.5*prob2(i,j+1);
 return P[i][j];
}

Colocando um contador em prob1 e prob2 podemos comparar o numero de chamadas :
numero de vitorias do jogador 1:2
numero de vitorias do jogador 2:3
numero de jogos:11
prob1: 0.401810
prob2: 0.401810
chamadas de prob1 48619
chamadas de prob2 145


sábado, 25 de agosto de 2012

Probabilidade - Paradoxo do aniversário



O paradoxo do aniversário é um resultado surpreendente na teoria das probabilidades. Este paradoxo garante que dado um grupo de 23 pessoas (ou mais) pessoas escolhidas aleatoriamente, a chance de que duas pessoas terão a mesma data de aniversário é de mais de 50%.  Em uma festa com 30 pessoas, a probabilidade de haver duas pessoas com o mesmo aniversário é maior que 70%. Vamos calcular estes valores!

Primeiramente, vamos calcular a número de pessoas de um grupo para que duas pessoas tenham a mesma data de aniversário com probabilidade acima de q.

Para isso, calcularemos a probabilidade de todas pessoas tenham data de aniversário diferente. Note que o evento de duas pessoas tenham a mesma da aniversário e todas as pessoas tenham data de aniversário diferente são complementares.

p(duas pessoas com o mesmo aniversario) =  1 - p(todas pessoas com aniversários diferente)

A probabilidade de duas pessoas terem aniversários diferentes é

$p = (\frac{365}{365})( \frac{364}{365}) = 0,997$

A probabilidade de três pesoas terem aniversários diferentes é:

$p = (\frac{365}{365}) (\frac{364}{365}) (\frac{363}{365})= 0,991$

A probabilidade de n pessoas terem aniversários diferentes é:
$p = (\frac{365}{365}) (\frac{365-1}{365}) (\frac{363-2}{365}) \ldots (\frac{365-(n-1)}{365})$


Para descobrir  número de pessoas de um grupo para que duas pessoas tenham a mesma data de aniversário com probabilidade acima de q. Basta calcular iterativamente p até que q < (1-p), ou seja, até a probabilidade do complemento de p supere  o nosso limite estabelecido.

$n(d,q) = $ número de pessoas em um grupo para que tenha duas pessoas com a mesma data de aniversário com probabilidade q considerando o ano com d dias.

int n(int d, double q){
 double p = 1.0;
 int    s = 1;
 while( q > (1.0-p) ){
  p = p*( (double)(d-s)/d);
  s++;
 }
 return s;
}

Podemos construir a seguinte tabela:


p
n(365,p)
10
10
20
14
30
17
40
20
50
23
60
27
70
30
80
35
90
41
100
167

Observe que o algoritmo mostra que em um grupo com 167 pessoas, duas pessoas terão a mesma data aniversário com probabilidade de 100%. Para que desse o resultado de 365 seria necessário um variável double com uma grande precisão.

A operação contrária é mais fácil:

$p(n,d)$ = probabilidade de duas pessoas terem o mesmo aniversário dado um grupo de n pessoas considerandoo ano com d dias.
double p(int n, int d){
 double r = 1.0;
 int    s = 1;
 while(s<n){
  r = r*( (double)(d-s)/d);
  s++;
 }
 return 1.0 - r;
}

Imagine que você monte uma playlist com 500 músicas, qual é a probabilidade de que você escute 100 músicas e ocorra uma música repetida?

p(100,500) = 0.9999758457
 
Imagine que você monte uma playlist com 500 músicas, quantas músicas você precisa escutar para que você escute uma música repetida com probabilidade de 50%?

 n(500,0.5) = 27



 







 

quarta-feira, 1 de fevereiro de 2012

randn usando randm

Um problema mais geral seria construir a função randn usando randm. Podemos separar em dois casos:
Caso 1: n < m
int randn(){
 int z;
 do{
   z = randm();
 }while(z>=m-(m%n));
 return z%n;
}

Caso 2: n> m
int randn(){
  do{
   z = randm()*m + randm();
  }while(z >= m*m - ( m*m % mmc(m,n)));
  return z%n;
}

terça-feira, 31 de janeiro de 2012

rand8 usando rand5

Você tem a função rand5() que gera valores no seguinte intervalo [0,1,2,3,4] com mesma probabilidade. Com essa função rand5, você precisa construir rand8() que gera valores no intervalo [0,1,2,3,4,5,6,7] com mesma probabilidade para cada.

Idéia 1:
Somar 2 duas execuções do rand5() e aplicar a operação % 8.
int rand8(){
  return (rand5()+rand5())%8;
}

Observe que quando o as duas execuções de rand5() são somadas, o valor dessa soma varia entre 0 até 8. Quando aplicamos a operação módulo 8, o valor fica no intervalo esperado mas com o seguinte problema:

Tabela das soma possíveis

+ 0 1 2 3 4
0 0 1 2 3 4
1 1 2 3 4 5
2 2 3 4 5 6
3 3 4 5 6 7
4 4 5 6 7 8

Note que a distribuição dos valores não é igual para todos os valores de soma possível.

Número 0 1 2 3 4 5 6 7
Ocorrência 2 2 3 4 5 4 3 2

A situação só piora quando a função rand5() é chamada mais vezes. Considere a seguinte idéia:

Idéia 2:
int rand8(){
  int i,s;
  s = 0;
  for(i=0;i<7;i++)
    s = s + rand5();
  return s%8;
}


Usando este programa abaixo, podemos contar o número de ocorrência de cada número:

int s[8];/*numero de ocorrencia de cada numero do rand8*/ 
int sum_rand5(int n, int sum){
  int j;
  if(n<7){
    for(j=0;j<5;j++){
      sum_rand5(n+1,sum+j);
    }
  }else{
    printf("%d\n",sum%8);
    s[sum%8]++;
  }
}
int main(){
  int i;
  for(i=0;i<8;i++) s[i] = 0;
  sum_rand5(0,0);
  for(i=0;i<8;i++)
    printf("%d %d\n",i,s[i]);
}

A tabela com o número de ocorrência gerado por esse programa é esta aqui:

Número 0 1 2 3 4 5 6 7
Ocorrência 9766 9681 9646 9681 9766 9850 9885 9850

Idéia 3:

Vamos construir o rand2 a partir do rand5. Usando o rand2, podemos construir o rand8() através do sistema binário da seguinte maneira:

int rand2(){
 int z;
 do{
   z = rand5();
 }while(z==4);
 if(z<2) return 0;
 else return 1; 
}
int rand8(){
  return rand2() + 2*rand2() + 4*rand2();
}


Idéia 4:

Executando o rand5 duas vezes, teremos 25 valores possíveis. Se eliminarmos 1 dos
valores possíveis, teremos um número de possibilidades que é múltiplo de 8. Falta saber como combinar as duas chamadas para que a ocorrência dos números no
intervalo [0,7] fiquem distribuídos igualmente.

Realize a seguinte operação:
int rand8(){
  do{
   z = rand5()*5 + rand5();
  }while(z==24);
  return z%8;
}
Tabela das soma possíveis módulo 8

+%8 0 1 2 3 4
0 0 1 2 3 4
5 5 6 7 0 1
10 2 3 4 5 6
15 7 0 1 2 3
20 4 5 6 7 0


Número de ocorrência de cada número

Número 0 1 2 3 4 5 6 7
Ocorrência 4 3 3 3 3 3 3 3

Observe que se retirarmos o valor 24, todos os valores passarão a ter o mesmo número de ocorrência. Observe também para construir o rand2, temos que descartar o 1 valor de 5 possíveis para garantir a equiprobalidade e essa função terá que ser chamada três vezes. Enquanto que nessa solução, teremos que descartar 1 valor de 25 possíveis.

Análise do número de chamadas realizadas Idéia 4

Vamos analisar o número de chamadas realizadas pelo último algoritmo.
p = 24/25 (a probabilidade que as duas chamadas de rand5 não precise ser descartada)
q = 1/25 (a probabilidade que as duas chamadas de rand5 precisem ser descartadas)

O número esperado de chamadas do algoritmo é dado pela seguinte fórmula:

N = 2p + 4qp + 6q^2p+ 8q^3p + 10q^4p+...

double p = 24/25.0;
double q = 1/25.0;
double acumulado = 1;
double s = 0.0;
for(i=1;i<=7;i++){
     s = s + 2*i*acumulado*p;
     acumulado*=q;
}
printf("%lf\n",s);

Valor encontrado: 2,083333

Análise do número de chamadas realizadas Idéia 2

Vamos analisar o número de chamadas realizadas pelo último algoritmo.
p = 64/125(a probabilidade que as três chamadas de rand2 não precise ser descartada)
q = 61/125 (a probabilidade que as três chamadas de rand5 precisem ser descartadas)

O número esperado de chamadas do algoritmo é dado pela seguinte fórmula:

N = 3p + 6qp + 9q^2p+ 12q^3p + 15q^4p+...

O valor encontrado pelo programa acima: 5,859375