====== Geração de números aleatórios ======
Números aleatórios são muito importantes em várias áreas da informática, como jogos, simulações e na geração de chaves criptográficas. Um gerador de **números pseudoaleatórios** (PRNG - [[https://en.wikipedia.org/wiki/Pseudorandom_number_generator|Pseudo Random Number Generator]]) é um algoritmo que gera uma sequência de valores aparentemente aleatórios. A sequência de números gerada por um PRNG não é realmente aleatória, porque pode ser reproduzida caso seja usado o mesmo valor inicial, usualmente chamado de "semente" (//seed//).((A reprodução da sequência pseudoaleatória não é um //bug//, mas uma característica útil, caso se deseje repetir os valores usados em uma simulação, ou repetir os passos que levaram o software a um erro, por exemplo.))
[[https://www.random.org/analysis/|{{https://www.random.org/analysis/dilbert.jpg}}]]
===== Congruência linear =====
Uma das abordagens mais simples para a geração de números pseudoaleatórios são os **geradores congruentes lineares** ([[https://en.wikipedia.org/wiki/Linear_congruential_generator|LCG]]), que são definidos através da seguinte fórmula:
> xi+1 = (A · xi + C) mod M
onde "x0" é o valor inicial (semente), A e C são parâmetros inteiros, M é o tamanho do espaço de valores aleatórios possíveis e "mod" é o resto da divisão inteira.
Por exemplo, escolhendo-se A=40.692, C=127, M=10.000.000 e x0=0, será gerada a seguinte sequência de números pseudoaleatórios:
> x0 = 0
> x1 = 127
> x2 = 5168011
> x3 = 6703739
> x4 = 8547515
> x5 = 5480507
> x6 = 2790971
> x7 = 192059
> x8 = 5264955
> x9 = 1548987
> ...
Os geradores congruentes lineares são considerados fracos, por serem facilmente previsíveis. Por isso, **não devem ser usados** em aplicações críticas, como a criptografia.
===== Atividade =====
==== a) Implementação da biblioteca ====
Construa uma biblioteca //LC-Random// para gerar números inteiros pseudoaleatórios usando o algoritmo de congruência linear; a interface dessa biblioteca é definida pelo seguinte arquivo de cabeçalho (que **não deve ser alterado**):
// ATENCAO: ESTE ARQUIVO NAO DEVE SER ALTERADO!
#ifndef __LCRANDOM__
#define __LCRANDOM__
// calcula e devolve um valor pseudoaleatório
unsigned long lcrandom () ;
// define o valor inicial (semente) da sequência de aleatórios
// (inicialmente zero (0) por default
void lcrandom_seed (unsigned long s) ;
// informa o valor máximo que pode ser gerado (o mínimo é sempre zero)
unsigned long lcrandom_max () ;
// modifica os parâmetros da equação do gerador; por default
// devem ser usados: A=40.692, C=127 e M=10.000.000
void lcrandom_parms (unsigned long A, unsigned long C, unsigned long M) ;
#endif
Caso a função ''lcrandom_parms'' não seja invocada, a implementação deve usar os valores //default// dos parâmetros A, C e M definidos no exemplo acima.
Dicas para a implementação:
* Os valores da semente, de x e dos parâmetros A, C e M são parte do **estado interno** da biblioteca, por isso devem ser implementados como variáveis globais dentro do arquivo ''lcrandom.c''.
* A biblioteca apenas calcula, atualiza e devolve valores; por isso, **nenhuma função de entrada/saída** (''printf'', ''scanf'', etc) deve ser chamada dentro da implementação de suas funções (a não ser para produzir eventuais mensagens de erro).
==== b) Geração de histograma ====
A distribuição dos valores produzidos por um gerador de números aleatórios pode ser avaliada visualmente através de um [[http://www.portalaction.com.br/estatistica-basica/16-histograma|histograma]]((existem diversas outras métricas melhores para avaliar a qualidade de um PRNG, algumas delas são descritas [[https://www.random.org/analysis/|nesta página]])).
Escreva um programa C para gerar um histograma dos valores aleatórios produzidos por sua biblioteca.
Para isso:
- gere 106 valores aleatórios no intervalo [0...99] (use ''lcrandom() % 100'');
- conte quantas vezes cada número foi gerado;
- normalize (i.e. ajuste) as contagens para intervalo [0..100], aplicando uma regra de três às contagens obtidas, na qual a maior contagem corresponde a 100;
- plote o histograma resultante em um gráfico em modo texto; no eixo vertical estão os valores possível (0 a 99, neste exemplo); no eixo horizontal estão as frequências de ocorrência normalizadas.
Exemplo simplificado de histograma em modo texto (cada ''*'' vale 2 unidades):
0 10 20 30 40 50 60 70 80 90 100
+----+----+----+----+----+----+----+----+----+----+
0 |*****************************
1 |**************************************************
2 |****************************************
3 |******************************************
4 |***********************************
5 |*******************************
6 |****************************
7 |******************************
...| (linhas omitidas)
98 |**********************************
99 |***********************************************
+----+----+----+----+----+----+----+----+----+----+
==== c) Cálculo de período ====
O **período** de um PRNG corresponde à quantidade de valores gerados por ele antes de começar a repeti-los, para uma dada semente. Por exemplo, para uma semente x0=0, um PRNG gera a seguinte sequência, cujo período é 16 (pois são gerados 16 valores distintos antes de repetir o 78):
> 0 2233 491 11 7219 **7801** 31 446 9876 831 6159 43259 63 38321 1126 43674 **7801** 31 446 9876 831 ...
Escreva um programa C para calcular o período do gerador implementado em sua biblioteca, a partir de x0=0.
Para os valores default de (A=40.692, C=127 e M=10.000.000), o período do gerador vale 62.503.
==== d) Tudo de novo, com novos parâmetros ====
Repita o cálculo do histograma e do período usando valores melhores para os parâmetros A, C e M do gerador: A = 1.103.515.245, C = 12.345 e M = 2.147.483.648 (231) M = 1.073.741.824 (230) - valores inspirados de [[https://en.wikipedia.org/wiki/Linear_congruential_generator|LCG]].
Para esses valores de A, C e M, o período do gerador deve ser igual a M.
Caso encontre erros do tipo ''relocation truncated to fit: R_X86_64_PC32 against symbol ...'' ao compilar seu código, experimente usar as opções ''-fPIC'' ou ''-mcmodel=medium'' na linha de compilação. Esses erros podem ocorrer no cálculo do período, caso um vetor muito grande.
==== Entregáveis ====
Arquivos a entregar ao professor:
- ''lcrandom.c'' : implementação das funções do gerador de aleatórios;
- ''histograma-1.c'' : implementação do histograma do gerador com os valores default de A, C e M;
- ''histograma-2.c'' : idem, com os valores melhores de A, C e M;
- ''periodo-1.c'' : implementação do cálculo de período do gerador com os valores default de A, C e M;
- ''periodo-2.c'' : idem, com os valores melhores de A, C e M.
**Lembre-se**:
* Identificar **TODOS** os seus arquivos (comentário com nome completo e GRR na primeira linha de cada arquivo).
* Compilar com a opção ''-Wall'' e resolver todos os //warnings//, pois eles penalizam a nota.
* Comentar seu código adequadamente (código sem comentários é penalizado).