Distribuição de probabilidade dos números da mega sena

Introdução

A loteria é uma modalidade de apostas difundida mundialmente, historicamente associada a práticas ancestrais de prever a sorte. No entanto, o interesse por jogos de azar impulsionou o desenvolvimento da teoria da probabilidade, uma ferramenta crucial para estimar as chances de eventos.

Neste contexto, analisamos a Mega Sena, modalidade de loteria mais popular no Brasil. Nosso objetivo é investigar se os números sorteados seguem um modelo probabilístico.

Implementação

Usamos dados históricos dos resultados da Mega Sena para realizar análises estatísticas e gráficas.

Bibliotecas

Carregamos as seguintes bibliotecas do R no RStudio, para adicionar funcionalidades extras:

  • tidyverse - para manipulação de dados;
  • readxl - para importar arquivos xlsx;
  • ggplot2 - para visualização gráfica;
  • gridExtra - para ampliar as possibilidades de manipulação gráfica;
  • DT - para incluir tabelas em relatórios Rmarkdown;
  • rmdformats - temas adicionais para documentos Rmarkdown.
library(tidyverse)
library (readxl)
library(ggplot2)
library(gridExtra)
library(DT)
library(rmdformats)

Coleta de dados

Os resultados da Mega Sena foram obtidos no site da CAIXA. Inicialmente, importamos e tratamos os dados.

Primeiro, criamos um objeto que armazena o caminho para acesso ao arquivo disponibilizado:

url <- 'https://servicebus2.caixa.gov.br/portaldeloterias/api/resultados/download?modalidade=Mega-Sena'

Depois de definido o diretório temporário, baixamos o arquivo no formato xlsx:

destfile <- tempfile ()
download.file (url, 
               destfile, 
               mode = 'wb', 
               method = "wget", 
               extra = "--no-check-certificate")

Tarefas de pré-processamento

Importação da planilha

Importamos a planilha do Excel para um objeto data frame:

resultados <- read_excel(destfile)

Descrição dos dados originais

Inspecionamos a estrutura básica do objeto que representa o conjunto de dados:

str(resultados)
## tibble [2,676 × 20] (S3: tbl_df/tbl/data.frame)
##  $ Concurso                                 : num [1:2676] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Data do Sorteio                          : chr [1:2676] "11/03/1996" "18/03/1996" "25/03/1996" "01/04/1996" ...
##  $ Bola1                                    : num [1:2676] 4 9 10 1 1 7 3 4 8 4 ...
##  $ Bola2                                    : num [1:2676] 5 37 11 5 2 13 5 17 43 18 ...
##  $ Bola3                                    : num [1:2676] 30 39 29 6 6 19 20 37 54 21 ...
##  $ Bola4                                    : num [1:2676] 33 41 30 27 16 22 21 38 55 25 ...
##  $ Bola5                                    : num [1:2676] 41 43 36 42 19 40 38 47 56 38 ...
##  $ Bola6                                    : num [1:2676] 52 49 47 59 46 47 56 53 60 57 ...
##  $ Ganhadores 6 acertos                     : num [1:2676] 0 1 2 0 0 0 0 0 0 0 ...
##  $ Cidade / UF                              : chr [1:2676] NA "PR" "RN; SP" NA ...
##  $ Rateio 6 acertos                         : chr [1:2676] "R$0,00" "R$2.307.162,23" "R$391.192,51" "R$0,00" ...
##  $ Ganhadores 5 acertos                     : num [1:2676] 17 65 62 39 98 109 100 60 17 251 ...
##  $ Rateio 5 acertos                         : chr [1:2676] "R$39.158,92" "R$14.424,02" "R$10.515,93" "R$15.322,24" ...
##  $ Ganhadores 4 acertos                     : num [1:2676] 2016 4488 4261 3311 5399 ...
##  $ Rateio 4 acertos                         : chr [1:2676] "R$330,21" "R$208,91" "R$153,01" "R$180,48" ...
##  $ Acumulado 6 acertos                      : chr [1:2676] "R$1.714.650,23" "R$0,00" "R$0,00" "R$717.080,75" ...
##  $ Arrecadação Total                        : chr [1:2676] "R$0,00" "R$0,00" "R$0,00" "R$0,00" ...
##  $ Estimativa prêmio                        : chr [1:2676] "R$0,00" "R$0,00" "R$0,00" "R$0,00" ...
##  $ Acumulado Sorteio Especial Mega da Virada: chr [1:2676] "R$0,00" "R$0,00" "R$0,00" "R$0,00" ...
##  $ Observação                               : chr [1:2676] NA NA NA NA ...

Tratamento dos dados

Selecionamos as colunas desejadas:

concursos <- (resultados[,c(1:8)])

Renomeamos a segunda coluna:

names(concursos)[2] <- "Data"

Verificamos se a quantidade de observações confere com o número do último concurso:

tail(concursos$Concurso, n = 1)
## [1] 2676

Averiguamos a existência de números de concursos ou datas repetidos:

sum(concursos[duplicated(concursos$Concurso), ])
## [1] 0
sum(concursos[duplicated(concursos$Data), ])
## [1] 0

Inquirimos a existência de valores nulos, em branco ou zerados em todo data frame :

sum(is.na(list(concursos)))
## [1] 0

Reduzimos a estrutura do data frame às dezenas sorteadas:

dezenas <- (concursos[,c(3:8)])
datatable(dezenas)

Análise exploratória dos dados

Espaços amostrais finitos e equiprováveis

Em uma distribuição uniforme, a probabilidade de um número isolado ser escolhido aleatoriamente é o quociente da equação P(X)=\(\frac{1}{N}\); nesse caso, P(X)=\(\frac{1}{60}\). Para eventos sem reposição, iteramos: P(Xi)=\(\frac{1}{N-(i-1)}\). Portanto, dado o espaço amostral representado pelo conjunto finito \(\Omega\) = {1, 2, 3, …, 60}, a sequência de resultados possíveis pode ser armazenada através da variável:

mega <- 1:60

O ganhador do concurso é aquele que acertar os 6 números sorteados. O resultado do sorteio é associado a uma combinação de eventos equiprováveis, supondo que todos os elementos do conjunto tenham a mesma chance de ocorrer. O número de combinações possíveis é calculado pela seguinte função:

choose(60,6)
## [1] 50063860

Tabela de frequência

Se a chance de ocorrer é a mesma para todos os números, considerando os eventos isolados sem reposição, espera-se uma distribuição uniforme dos números sorteados até o último certame. No entanto, destacamos que cada sorteio é independente dos anteriores, mesmo que a distribuição geral seja uniforme. Portanto, não podemos estabelecer padrões em curto prazo.

Uma tabela de frequência ajuda a lançar luz sobre os dados, ao fornecer a contagem das observações:

names(dezenas) <- NULL
dezenas <- unlist(dezenas)
dzn <- as.data.frame(dezenas)
table(dzn)
## dezenas
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
## 257 268 248 285 297 268 250 271 251 312 276 256 274 261 231 276 280 258 252 259 
##  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40 
## 227 243 288 268 259 225 279 279 277 285 250 284 286 287 285 275 289 282 257 250 
##  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
## 285 283 279 282 260 270 251 247 275 259 273 267 307 276 226 281 254 262 259 255

Podemos averiguar as percentagens ou proporções de cada observação em relação ao total da amostra:

prop.table(table(dzn))
## dezenas
##          1          2          3          4          5          6          7 
## 0.01600648 0.01669158 0.01544594 0.01775037 0.01849776 0.01669158 0.01557050 
##          8          9         10         11         12         13         14 
## 0.01687843 0.01563279 0.01943199 0.01718984 0.01594420 0.01706527 0.01625561 
##         15         16         17         18         19         20         21 
## 0.01438714 0.01718984 0.01743896 0.01606876 0.01569507 0.01613104 0.01413802 
##         22         23         24         25         26         27         28 
## 0.01513453 0.01793722 0.01669158 0.01613104 0.01401345 0.01737668 0.01737668 
##         29         30         31         32         33         34         35 
## 0.01725212 0.01775037 0.01557050 0.01768809 0.01781266 0.01787494 0.01775037 
##         36         37         38         39         40         41         42 
## 0.01712755 0.01799950 0.01756353 0.01600648 0.01557050 0.01775037 0.01762581 
##         43         44         45         46         47         48         49 
## 0.01737668 0.01756353 0.01619332 0.01681614 0.01563279 0.01538366 0.01712755 
##         50         51         52         53         54         55         56 
## 0.01613104 0.01700299 0.01662930 0.01912058 0.01718984 0.01407573 0.01750125 
##         57         58         59         60 
## 0.01581963 0.01631789 0.01613104 0.01588191

Histograma de frequência

A contagem das observações pode ser melhor visualizada através de um histograma de frequência:

ggplot(dzn, aes(x = dezenas)) + 
  geom_histogram(aes(y = ..count..), bins = 300) + 
  scale_x_continuous(breaks=seq(1,60,1)) + 
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 
  xlab("dezenas sorteadas")

Observamos a tendência de certos números serem mais ou menos frequentes ao longo dos concursos.

A ordem importa

Ao consideramos eventos sem reposição, é importante ajustar o tamanho do espaço amostral para refletir o decremento unitário, depois do sorteio da primeira dezena.

A fórmula para cálculo do espaço amostral ajustado é dada pela expressão:

\(A(i)\)=\({N-(i-1)}\)

A probabilidade de um número específico ser sorteado aumenta, especialmente para as dezenas posteriores à primeira.

No entanto, ao examinarmos a quantidade de elementos distintos, únicos, existentes nos conjuntos definidos pelos números sorteados de acordo com ordem das bolas, constatamos que alguns tendem a repetir números com mais frequência.

sapply(concursos, function(x) n_distinct(x))
## Concurso     Data    Bola1    Bola2    Bola3    Bola4    Bola5    Bola6 
##     2676     2676       43       48       53       51       48       38

Variação temporal

Investigamos se há variação nas frequências dos números sorteados ao longo do tempo, para revelar padrões sazonais ou mudanças nas dinâmicas dos sorteios:

# Convertemos a coluna Data para o formato de data
ggplot(concursos, aes(x = Concurso, y = Bola1, color = "Bola 1")) +
  geom_line() + 
  geom_line(aes(y = Bola2, color = "Bola 2")) +
  geom_line(aes(y = Bola3, color = "Bola 3")) +
  geom_line(aes(y = Bola4, color = "Bola 4")) +
  geom_line(aes(y = Bola5, color = "Bola 5")) +
  geom_line(aes(y = Bola6, color = "Bola 6")) +
  scale_color_manual(values = c("Bola 1" = "lightblue", 
                                "Bola 2" = "darkblue",
                                "Bola 3" = "lightgreen",
                                "Bola 4" = "darkgreen",
                                "Bola 5" = "lightcoral",
                                "Bola 6" = "darkred")) +
  labs(title = "Variação dos Números Sorteados ao Longo do Tempo",
       x = "Concurso", y = "Número Sorteado")

Análise de tendências

Avaliamos se existem tendências perceptíveis nos números sorteados, como grupos de números que têm maior probabilidade de serem escolhidos em determinados períodos.

Vamos criar uma série temporal para os números sorteados na Bola 1, para analisar se podem ser revelados padrões ou comportamentos específicos associados à primeira dezena ao longo dos concursos.

time_series <- table(concursos$Bola1)

df_time_series <- as.data.frame(time_series)

plot(df_time_series$Var1, 
     df_time_series$Freq, type="1",
     col="blue",
     xlab="Número Sorteado",
     ylab="Frequência",
     main="Análise de Tendências")

Análise de correlações entre os números

Analisamos correlações entre os números sorteados um mesmo concurso, para verificar possíveis associações que possam influenciar os resultados.

cor_matrix <- cor(concursos[, c("Bola1",
                              "Bola2",
                              "Bola3",
                              "Bola4",
                              "Bola5",
                              "Bola6")])

heatmap(cor_matrix,
        main="Correlação entre Números Sorteados",
        col=colorRampPalette(c("blue", "white", "red"))(25))

Análise do desvio padrão

Podemos utilizar outras métricas estatísticas, como desvio padrão, para uma compreensão mais abrangente da distribuição dos números:

std_dev <- apply(concursos[, 
                           c("Bola1",
                             "Bola2",
                             "Bola3",
                             "Bola4",
                             "Bola5",
                             "Bola6")], 1, sd)

hist(std_dev,
     col="green",
     xlab="Desvio Padrão",
     main="Distribuição do Desvio Padrão dos Números Sorteados")

Teorema do Limite Central

Soma dos Números Sorteados

Analisamos a soma dos números sorteados na Mega Sena, aplicando o Teorema do Limite Central (TLC). Este teorema é um princípio estatístico fundamental que descreve o comportamento das médias amostrais, independentemente da distribuição original.

Ao calcular a soma dos números sorteados em cada concurso, efetivamente calculamos uma média amostral dessas somas. O TLC nos permite inferir que, à medida que o número de concursos aumenta, a distribuição das somas se aproxima de uma distribuição normal, mesmo que a distribuição original dos números sorteados não o seja.

Introduzimos uma nova coluna para armazenar a soma dos números sorteados em cada concurso, pois isso proporciona uma visão mais clara dos padrões, especialmente à medida que o tamanho da amostra aumenta.

concursos$Soma <- rowSums(concursos[,c(3:8)])

O padrão da distribuição sugere que tenham sido sorteados aleatoriamente:

h1 <- ggplot(concursos) + 
        geom_histogram(aes(x=Soma), binwidth=10, fill="gray") + 
        xlab("soma dos números sorteados") + 
        ylab("")
h1

Experimento teórico

Conduzimos um experimento teórico para simular a escolha aleatória de números e comparar com os resultados reais.

sortear <- function() {
  mega <- 1:60
 bolas <- sample(mega, size = 6, replace = FALSE)
      sum(bolas)
}

vezes <- last(concursos$Concurso)

sorteios <- (replicate(vezes, sortear()))

sorteios <- as.data.frame(sorteios)

Geramos o gráfico da distribuição:

h2 <- ggplot(sorteios) + 
        geom_histogram(aes(x=sorteios), binwidth=10, fill="gray") + 
        xlab("soma dos números simulados") + 
        ylab("")
h2

Dispostos lado a lado, podemos comparar graficamente o comportamento dos dados observados nos sorteios com o comportamento dos dados do experimento teórico:

grid.arrange(h1, h2, ncol=2)

Teste Qui-Quadrado

Se ainda assim hesitamos em determinar se os números foram selecionados de acordo com o modelo não-determinístico, aplicamos o Teste Qui-Quadrado, utilizado para testar a aderência de um modelo probabilístico a um conjunto de dados observados:

soma <- concursos$Soma
chisq.test(soma)
## 
##  Chi-squared test for given probabilities
## 
## data:  soma
## X-squared = 23454, df = 2675, p-value < 2.2e-16

Conclusão

A notação 2.2e-16 representa 0.00000000000000022 em formato científico.

Quando o Teste Qui-Quadrado resulta em um p-valor muito baixo, próximo a zero, isso geralmente indica evidências significativas para rejeitar a hipótese nula.

Portanto, podemos concluir que os sorteios da Mega Sena são consistentes com um modelo de escolha aleatória dos números, ou seja, seguem um processo probabilístico. Assim, os resultados não apresentam um padrão determinístico previsível.

Referências:

Grolemund, G. (2014). Hands-On Programming with R.
Ross, K. (2020). An Introduction to Probability and Simulation.
Templ, M. (2016). Simulation for Data Science with R.