5  Técnica da Inversão para Variáveis Discretas

A técnica da inversão é uma maneira poderosa de gerar variáveis aleatórias (v.a.) a partir de uma distribuição arbitrária, usando números aleatórios uniformes no intervalo \([0, 1)\). A ideia básica é usar a função de distribuição acumulada (CDF) para mapear um número uniforme gerado entre 0 e 1 para o valor correspondente da v.a. discreta.

5.1 Inversa da CDF

O método da inversão recebe esse nome pois seu algoritmo também pode ser caracterizado pela função inversa da CDF. A inversa da CDF (função de distribuição acumulada), também conhecida como a função quantil, é definida da seguinte forma:

Definição: Seja \(F(x)\) a função de distribuição acumulada (CDF) de uma variável aleatória \(X\). A inversa da CDF, denotada por \(F^{-1}(p)\), é definida como:

\[ F^{-1}(p) = \inf \{ x \in \mathbb{R} : F(x) \geq p \}, \quad \text{para } p \in [0, 1] \]

Em palavras: - A inversa da CDF \(F^{-1}(p)\) mapeia um número \(p\), que representa uma probabilidade acumulada, de volta ao valor \(x\) correspondente da variável aleatória \(X\), tal que a probabilidade acumulada até \(x\) é igual a \(p\). - Isso significa que, se \(p = F(x)\), então \(F^{-1}(p) = x\).

Assim, o menor valor \(x_i\) tal que \(F(x_i) \geq u\) é justamente \(F^{-1}(u)\)

5.2 Geração de Variáveis Aleatórias Discretas Genéricas

Considere uma v.a. discreta \(X\) que assume valores em em \(x_1,x_2,\ldots\). Dadas as probabilidades de cada um desses valores \(p(x_i)\), a CDF \(F(x)\) é definida como:

\[ F(x_i) = \sum_{j=1}^{i} p(x_j) \]

O algoritmo da inversão para gerar uma variável aleatória discreta genérica é:

  1. Gerar um número aleatório com distribuição uniforme \(u \in [0, 1)\).

  2. Encontrar o menor valor \(x_i\) tal que \(F(x_i) \geq u\).

  3. Retornar \(x_i\).

Mostrar código
# Exemplo de valores e probabilidades de uma variável aleatória discreta
valores <- c(0, 1, 2, 3, 4, 5, 6)
probabilidades <- c(0, 0.1, 0.2, 0.3, 0.25, 0.15, 0) 

# Calculando a CDF
cdf <- cumsum(probabilidades)

# Gerando um número aleatório uniforme
u <- runif(1)

# Encontrando o valor correspondente na CDF
valor_gerado <- NA
for (i in seq_along(valores)) {
  if (u <= cdf[i]) {
    valor_gerado <- valores[i]
    break
  }
}

# Ajustando o gráfico para corrigir a visualização da CDF e garantir que os valores estejam corretamente posicionados
library(ggplot2)

# Criando um dataframe para os valores e CDF
df <- data.frame(valores = valores, cdf = cdf)

# Gráfico da CDF com número aleatório e valor gerado
ggplot(df, aes(x = valores, y = cdf)) +
  geom_step(direction = "hv", color = "blue", size = 1.5) +
  geom_hline(yintercept = u, color = "red", linetype = "dashed") +
  geom_vline(xintercept = valor_gerado, color = "green", linetype = "dashed") +
  labs(title = "Técnica da Inversão para Geração de Variável Aleatória Discreta",
       x = "Valores da Variável Aleatória", 
       y = "CDF") +
  annotate("text", x = max(valores), y = u, label = sprintf("u = %.2f", u), hjust = -0.1, vjust = -1) +
  annotate("text", x = valor_gerado, y = max(cdf), label = paste("Valor gerado =", valor_gerado), hjust = -0.1, vjust = -0.5) +
  theme_minimal() +
  theme(panel.grid = element_blank())

Mostrar código
import numpy as np
import matplotlib.pyplot as plt
 
# Exemplo de valores e probabilidades de uma variável aleatória discreta
valores = [0, 1, 2, 3, 4, 5, 6]
probabilidades = [0, 0.1, 0.2, 0.3, 0.25, 0.15, 0]

# Calculando a CDF
cdf = np.cumsum(probabilidades)

# Gerando um número aleatório uniforme
u = np.random.uniform(0, 1)

# Encontre o valor correspondente na CDF
valor_gerado = None
for i, valor in enumerate(valores):
    if u < cdf[i]:
        valor_gerado = valor
        break
# Ajustando o gráfico para corrigir a visualização da CDF e garantir que os valores estejam corretamente posicionados
plt.figure(figsize=(10, 6))

# Ajustando o eixo x para que a CDF comece e termine corretamente
plt.step(valores, cdf, label='CDF', color='blue', linewidth=2, where='post')
plt.axhline(y=u, color='red', linestyle='--', label=f'Número aleatório u = {u:.2f}')
plt.axvline(x=valor_gerado, color='green', linestyle='--', label=f'Valor gerado = {valor_gerado}')
plt.title('Técnica da Inversão para Geração de Variável Aleatória Discreta')
plt.xlabel('Valores da Variável Aleatória')
plt.ylabel('CDF')
plt.legend()
plt.grid(True)
plt.show()

5.3 Prova de que o método funciona

Enunciado. Considere uma v.a. discreta \(X\) com suporte ordenado \(x_1<x_2<\cdots\) e massas \(p_i=\mathbb{P}(X=x_i)\). Defina \(F(x_i)=\sum_{j\le i}p_j\) e \(F(x)=0\) para \(x<x_1\). Seja \(U\sim\mathrm{Unif}(0,1)\) e \[ \tilde{X}=\min\{x_i:\,F(x_i)\ge U\}. \] Então \(\tilde{X}\) tem a mesma distribuição de \(X\).

Demonstração. Para cada \(i\ge1\), \[ \{\tilde{X}=x_i\}=\{F(x_{i-1})<U\le F(x_i)\}, \] onde \(F(x_0)=0\). Logo, \[ \mathbb{P}(\tilde{X}=x_i)=F(x_i)-F(x_{i-1})=p_i. \] Portanto, \(\tilde{X}\) reproduz exatamente a PMF desejada.

5.4 Exemplo 1: Geração de uma Bernoulli por inversão

Para \(X\sim\mathrm{Bernoulli}(p)\), temos \(\mathbb P(X=0)=1-p\), \(\mathbb P(X=1)=p\) e

\[ F(0)=1-p,\quad F(1)=1. \]

Assim, o mesmo da inversão consiste em gerar \(U\sim\mathrm{Unif}(0,1)\) e definir

\[ X=F^{-1}(U)= \begin{cases} 1, & \text{if } U>1-p,\\[4pt] 0, & \text{if } U\le 1-p. \end{cases} \]

Abaixo, simulamos \(n\) observações, calculamos a distribuição empírica associada a elas e a comparamos com a real de uma Bernoulli.

Mostrar código
set.seed(123)

# parâmetros
p <- 0.3
n <- 10000

# simulação por inversão
u <- runif(n)
x <- as.integer(u <= p)  # 1 se U<=p, senão 0

# distribuição empírica (garantindo níveis 0 e 1)
emp <- prop.table(table(factor(x, levels = c(0, 1))))
real <- c(`0` = 1 - p, `1` = p)

# tabela de comparação
comp <- data.frame(
  valor = c(0, 1),
  real = as.numeric(real),
  estimada = as.numeric(emp)
)
comp
  valor real estimada
1     0  0.7   0.6988
2     1  0.3   0.3012
Mostrar código
# gráfico comparando real vs estimada
library(tidyr); library(ggplot2)
comp_long <- pivot_longer(comp, cols = c(real, estimada),
                          names_to = "tipo", values_to = "prob")

ggplot(comp_long, aes(x = factor(valor), y = prob, fill = tipo)) +
  geom_col(position = "dodge", color = "black") +
  scale_x_discrete(name = "Valor de X") +
  labs(title = sprintf("Bernoulli(p) com p = %.2f — n = %d", p, n),
       y = "Probabilidade") +
  theme_minimal() +
  theme(panel.grid.major = element_blank(), legend.title = element_blank())

Mostrar código
import numpy as np
import matplotlib.pyplot as plt

# parâmetros
p = 0.3
n = 10000

# simulação por inversão
u = np.random.rand(n)
x = (u <= p).astype(int)  # 1 se U<=p, senão 0

# distribuição empírica (garantindo níveis 0 e 1)
counts = np.bincount(x, minlength=2)
emp = counts / n
real = np.array([1 - p, p])

# tabela de comparação (impresso no console)
print("valor | real   | estimada")
valor | real   | estimada
Mostrar código
for v in [0, 1]:
    print(f"{v:5d} | {real[v]:.4f} | {emp[v]:.4f}")
    0 | 0.7000 | 0.7057
    1 | 0.3000 | 0.2943
Mostrar código
# gráfico comparando real vs estimada
vals = np.array([0, 1], dtype=float)
width = 0.35

plt.figure(figsize=(8, 5))
plt.bar(vals - width/2, real, width, edgecolor='black', label='real')
plt.bar(vals + width/2, emp,  width, edgecolor='black', label='estimada')

plt.xticks(vals, ['0', '1'])
([<matplotlib.axis.XTick object at 0x7558a291f3b0>, <matplotlib.axis.XTick object at 0x7558a2900ec0>], [Text(0.0, 0, '0'), Text(1.0, 0, '1')])
Mostrar código
plt.xlabel('Valor de X')
plt.ylabel('Probabilidade')
plt.title(f'Bernoulli(p) com p = {p:.2f} — n = {n}')
plt.legend()
plt.grid(True, axis='y')
plt.show()

5.5 Exemplo 2: Geração de Variáveis Aleatórias com Distribuição Geométrica

A distribuição geométrica modela o número de falhas até o primeiro sucesso em uma sequência de experimentos de Bernoulli. Se a probabilidade de sucesso em cada tentativa é \(p\), a PMF é dada por: \[ P(X = k) = (1 - p)^{k} p \quad \text{para} \quad k = 0, 1, 2 \dots \] onde \(k\) representa o número de falhas antes do primeiro sucesso.

Vamos agora obter a fórmula da inversa da CDF para a distribuição geométrica foi obtida a partir da função de distribuição acumulada (CDF) da distribuição geométrica:

\[ \begin{aligned} F(k) & = \mathbb{P}(X \leq k) = \sum_{x=0}^{k}(1 - p)^{x}p \\ &= \frac{p}{1 - p} \sum_{x=0}^{k} (1 - p)^x\\ & = \frac{p}{1 - p} \cdot \frac{(1 - p)(1 - (1 - p)^{k+1})}{p}\\ &= 1 - (1 - p)^{k+1}. \end{aligned} \] Queremos encontrar a inversa da CDF, ou seja, a fórmula que, dado um valor \(u\) entre 0 e 1, nos permita calcular o valor \(k\) tal que \(F(k) = u\).

Sabemos que:

\[ u = F(k) = 1 - (1 - p)^{k+1} \]

Nosso objetivo é resolver essa equação para \(k\). Vamos fazer isso passo a passo.

Começamos isolando o termo \((1 - p)^{k+1}\):

\[ u = 1 - (1 - p)^{k+1} \]

Subtraindo 1 de ambos os lados:

\[ u - 1 = - (1 - p)^{k+1} \]

Multiplicando ambos os lados por \(-1\):

\[ 1 - u = (1 - p)^{k+1} \]

Agora aplicamos o logaritmo natural (log base \(e\)) em ambos os lados para resolver \(k\):

\[ \log(1 - u) = \log((1 - p)^{k+1}) \]

Usando a propriedade dos logaritmos que permite trazer o expoente \(k\) para frente:

\[ \log(1 - u) = (k+1) \cdot \log(1 - p) \]

Agora, isolamos \(k\) e levamos em conta que ele deve ser inteiro, temos:

\[ k = \left\lceil \frac{\log(1 - u)}{\log(1 - p)} -1 \right\rceil \]

Podemos agora gerar variáveis aleatórias com distribuição geométrica a partir de um número aleatório uniforme \(u \in [0, 1)\).

Mostrar código
# Função para gerar a inversa da CDF para a distribuição geométrica
inversa_cdf_geometrica <- function(p, u) {
  # k = falhas antes do 1º sucesso
  k <- ceiling(log1p(-u) / log1p(-p)) - 1L
  as.integer(k)
}
# Parâmetro p da distribuição geométrica
p <- 0.5

# Gerando 1000 números uniformemente distribuídos
uniformes <- runif(1000)

# Gerando a variável aleatória geométrica correspondente para cada número uniforme
geometricas <- sapply(uniformes, inversa_cdf_geometrica, p = p)

# Plotando um histograma das variáveis geométricas geradas
library(ggplot2)

df <- data.frame(geometricas = geometricas)
ggplot(df, aes(x = geometricas)) +
  geom_histogram(bins = max(geometricas) + 1, color = "black", fill = "skyblue", boundary = 0, closed = "left") +
  labs(title = "Histograma de Variáveis Aleatórias Geométricas Usando a Inversa da CDF",
       x = "Valor da Variável Aleatória Geométrica", 
       y = "Frequência") +
  theme_minimal() +
  theme(panel.grid.major = element_blank())

Mostrar código
# Função para calcular a CDF da distribuição geométrica
cdf_geometrica <- function(k, p) {
  1 - (1 - p)^(k + 1)
}

# Gerando valores de k para plotar a CDF
k_values <- 0:20
cdf_values <- sapply(k_values, cdf_geometrica, p = p)

# Plotando a CDF da distribuição geométrica
df_cdf <- data.frame(k_values = k_values, cdf_values = cdf_values)
ggplot(df_cdf, aes(x = k_values, y = cdf_values)) +
  geom_step(direction = "hv", color = "blue", size = 1.5) +
  labs(title = "CDF da Distribuição Geométrica (p = 0.5)",
       x = "k (Número de tentativas até o primeiro sucesso)", 
       y = "F(k)") +
  theme_minimal() +
  theme(panel.grid.major = element_blank())

Mostrar código
import numpy as np
import math as math
import matplotlib.pyplot as plt

# Função para gerar a inversa da CDF para a distribuição geométrica
def inversa_cdf_geometrica(p, u):
    # k = falhas antes do 1º sucesso
    k = math.log1p(-u) / math.log1p(-p)
    return int(k)

# Parâmetro p da distribuição geométrica
p = 0.5

# Gerando 1000 números uniformemente distribuídos
uniformes = np.random.uniform(0, 1, 1000)

# Gerando a variável aleatória geométrica correspondente para cada número uniforme
geometricas = [inversa_cdf_geometrica(p, u) for u in uniformes]

# Plotando um histograma das variáveis geométricas geradas
plt.figure(figsize=(10, 6))
plt.hist(geometricas,
         bins=range(0, max(geometricas) + 2),  # inclui o último intervalo
         edgecolor='black', align='left')
plt.title('Histograma de Variáveis Aleatórias Geométricas Usando a Inversa da CDF')
plt.xlabel('Valor da Variável Aleatória Geométrica')
plt.ylabel('Frequência')
plt.grid(True)
plt.show()

Mostrar código
# Calculando a CDF para a distribuição geométrica
def cdf_geometrica(k, p):
    return 1 - (1 - p)**k

# Gerando valores de k para plotar a CDF
k_values = np.arange(1, 21)
cdf_values = [cdf_geometrica(k, p) for k in k_values]

# Plotando a CDF da distribuição geométrica
plt.figure(figsize=(10, 6))
plt.step(k_values, cdf_values, where='post', color='blue', label='CDF Geométrica', linewidth=2)
plt.title('CDF da Distribuição Geométrica (p = 0.5)')
plt.xlabel('k (Número de tentativas até o primeiro sucesso)')
plt.ylabel('F(k)')
plt.grid(True)
plt.legend()
plt.show()

5.6 Exemplo 3: Geração de Variáveis Aleatórias com Distribuição Poisson

A distribuição de Poisson é usada para modelar o número de eventos que ocorrem em um intervalo de tempo ou espaço fixo, onde os eventos ocorrem com uma taxa constante \(\lambda\) e de forma independente.

A função de probabilidade de massa (PMF) da distribuição de Poisson é dada por:

\[ P(X = k) = \frac{\lambda^k e^{-\lambda}}{k!}, \quad k = 0, 1, 2, \ldots \]

A distribuição de Poisson tem uma fórmula recursiva que pode ser usada para calcular as probabilidades de forma mais eficiente. Em vez de recalcular a probabilidade \(P(X = k)\) a cada vez, podemos usar a seguinte relação recursiva:

\[ P(X = k+1) = \frac{\lambda}{k+1} \cdot P(X = k) \]

Onde \(P(X = 0) = e^{-\lambda}\).

Essa relação recursiva permite gerar variáveis aleatórias de Poisson sem precisar calcular fatoriais repetidamente, o que é mais eficiente para grandes valores de \(\lambda\) ou grandes números de eventos \(k\).

5.6.1 Técnica da Inversão Usando a Fórmula Recursiva

Para gerar uma variável aleatória de Poisson usando a técnica da inversão e a fórmula recursiva, o processo é o seguinte:

  1. Seja \(U \sim \text{Unif}(0,1)\)
  2. Faça \(i = 0\), \(p = e^{-\lambda}\) e \(F = p\)
  3. Se \(U \leq F\), faça \(X = i\) e pare.
  4. Senão, atualize \(p = \frac{\lambda p}{i + 1}\), \(F = F + p\) e \(i = i + 1\)
  5. Volte para o passo (3)

A seguir implementamos esse método:

Mostrar código
# Função para gerar a inversa da CDF para a distribuição Poisson usando a técnica de inversão e a fórmula recursiva
inversa_cdf_poisson_recursiva <- function(lam, u) {
  k <- 0
  p <- exp(-lam)  # P(X=0)
  F_acm <- p  # Iniciamos com a probabilidade P(X=0)
  
  # Continuamos somando até que F >= u
  while (u > F_acm) {
    k <- k + 1
    p <- p * lam / k  # Atualiza a probabilidade recursivamente para o próximo valor
    F_acm <- F_acm + p
  }
  
  return(k)
}

# Parâmetro lambda da distribuição Poisson
lam <- 3

# Gerando 1000 números uniformemente distribuídos
uniformes <- runif(1000)

# Gerando a variável aleatória Poisson correspondente para cada número uniforme
poisson_vars <- sapply(uniformes, inversa_cdf_poisson_recursiva, lam = lam)

# Plotando o histograma das variáveis Poisson geradas
library(ggplot2)

df <- data.frame(poisson_vars = poisson_vars)
ggplot(df, aes(x = poisson_vars)) +
  geom_histogram(bins = max(poisson_vars) + 1, color = "black", fill = "skyblue", boundary = 0, closed = "left") +
  labs(title = "Histograma de Variáveis Aleatórias Poisson Usando a Fórmula Recursiva (λ = 3)",
       x = "Valor da Variável Aleatória Poisson", 
       y = "Frequência") +
  theme_minimal() +
  theme(panel.grid.major = element_blank())

Mostrar código
# Função para calcular a CDF da distribuição Poisson
cdf_poisson <- function(k, lam) {
  cdf <- 0
  p <- exp(-lam)  # P(X=0)
  for (i in 0:k) {
    cdf <- cdf + p  # Adiciona a probabilidade à CDF
    if (i < k) {
      p <- p * lam / (i + 1)  # Atualiza a probabilidade recursivamente
    }
  }
  return(cdf)
}

# Gerando valores de k para a CDF
k_values <- 0:14
cdf_values <- sapply(k_values, cdf_poisson, lam = lam)

# Plotando a CDF da distribuição Poisson
df_cdf <- data.frame(k_values = k_values, cdf_values = cdf_values)
ggplot(df_cdf, aes(x = k_values, y = cdf_values)) +
  geom_step(direction = "hv", color = "blue", size = 1.5) +
  labs(title = "CDF da Distribuição Poisson Usando a Fórmula Recursiva (λ = 3)",
       x = "k (Número de eventos)", 
       y = "F(k)") +
  theme_minimal() +
  theme(panel.grid.major = element_blank())

Mostrar código
import numpy as np
import matplotlib.pyplot as plt
import math

# Função para gerar a inversa da CDF para a distribuição Poisson usando a técnica de inversão e a fórmula recursiva
def inversa_cdf_poisson_recursiva(lam, u):
    k = 0
    p = math.exp(-lam)  # P(X=0)
    F = p  # Iniciamos com a probabilidade P(X=0)
    
    # Continuamos somando até que F >= u
    while u > F:
        k += 1
        p = p * lam / k  # Atualiza a probabilidade recursivamente para o próximo valor
        F += p
    
    return k

# Parâmetro lambda da distribuição Poisson
lam = 3

# Gerando 1000 números uniformemente distribuídos
uniformes = np.random.uniform(0, 1, 1000)

# Gerando a variável aleatória Poisson correspondente para cada número uniforme
poisson_vars = [inversa_cdf_poisson_recursiva(lam, u) for u in uniformes]

# Plotando o histograma das variáveis Poisson geradas
plt.figure(figsize=(10, 6))
plt.hist(poisson_vars, bins=range(0, max(poisson_vars) + 1), color='skyblue', edgecolor='black', align='left')
plt.title('Histograma de Variáveis Aleatórias Poisson Usando a Fórmula Recursiva (λ = 3)')
plt.xlabel('Valor da Variável Aleatória Poisson')
plt.ylabel('Frequência')
plt.grid(True)
plt.show()

Mostrar código
# Calculando a CDF da distribuição Poisson
def cdf_poisson(k, lam):
    cdf = 0
    p = math.exp(-lam)  # P(X=0)
    for i in range(k+1):
        cdf += p  # Adiciona a probabilidade à CDF
        if i < k:  # Atualiza a probabilidade recursivamente
            p = p * lam / (i + 1)
    return cdf

# Gerando valores de k para a CDF
k_values = np.arange(0, 15)
cdf_values = [cdf_poisson(k, lam) for k in k_values]

# Plotando a CDF da distribuição Poisson
plt.figure(figsize=(10, 6))
plt.step(k_values, cdf_values, where='post', color='blue', label='CDF Poisson', linewidth=2)
plt.title('CDF da Distribuição Poisson Usando a Fórmula Recursiva (λ = 3)')
plt.xlabel('k (Número de eventos)')
plt.ylabel('F(k)')
plt.grid(True)
plt.legend()
plt.show()

5.7 Exercícios

Exercício 1. Seja \(X\) uma v.a. tal que \(\mathbb P(X=1)=0.3,\mathbb P(X=3)=0.1\) e \(\mathbb P(X=4)=0.6\).

  1. Escreva um pseudo-algoritmo para gerar um valor de \(X\).

  2. Implemente uma função para gerar \(n\) valores de \(X\).

  3. Compare a distribuição das frequências obtidas na amostra simulada com as probabilidades reais.

Exercício 2. Considere \(X\) uma v.a. tal que

\[ \mathbb{P}(X=i) = \alpha \mathbb{P}(X_1=i) + (1-\alpha) \mathbb{P}(X_2=i), \quad i=0,1,\dots \]

onde \(0 \leq \alpha \leq 1\) e \(X_1, X_2\) são v.a. discretas.

A distribuição de \(X\) é chamada de distribuição de mistura. Podemos escrever

\[ X = \begin{cases} X_1, & \text{com probabilidade } \alpha \\ X_2, & \text{com probabilidade } 1-\alpha \end{cases} \]

Implemente um algoritmo para gerar uma amostra de tamanho \(n\) da distribuição mistura de uma Poisson e de uma Geométrica com base nas funções implementadas nos exemplos (2) e (3).