Skip to content

06 - Algoritmos Aleatorizados

Um gerador de números pseudo-aleatórios (RNG) é um algoritmo determinístico que gera uma sequência de números que parece aleatória. Essa frase possui dois termos importantes que precisamos destrinchar:

  • determinístico: Um RNG tipicamente recebe como entrada um inteiro seed (que representa uma sequência de bits "aleatória") e gera uma sequência de números baseada no seed. Ou seja, o algoritmo é determinístico pois gera sempre a mesma sequência para uma determinada entrada (seed).
  • parece aleatória: Se compararmos duas sequências de números, uma gerada por um RNG e outra por uma distribuição uniforme de verdade, não conseguimos dizer qual sequência foi gerada pelo RNG.

Ou seja, ao escolhermos um seed a sequência gerada será sempre a mesma, mesmo se executarmos o programa em outras máquinas. Isso torna a utilização de RNGs para experimentos bastante interessante: é possível reproduzir os resultados feitos por outros desenvolvedores/cientistas. Para isto é necessário

  1. que o programa permita escolher o seed da simulação;
  2. que o seed usado seja publicado junto com os resultados.

Question

E se quisermos gerar uma sequência diferente a cada execução do programa? Como poderíamos configurar o seed para que isto aconteça?

Iniciando com RNGs

Muitas implementações de RNGs são divididas em duas partes:

  1. engine/random state: algoritmo que gera um inteiro cujos bits formam uma sequência pseudo-aleatória.
  2. distribution: utiliza os bits acima para retornar números que sigam alguma distribuição estatística (como normal ou uniforme).

Question

A biblioteca padrão de C++ disponibiliza diversas funções para utilização de *RNG*s (cabeçalho <random> - documentação neste link). Se você quisesse sortear números aleatórios inteiros entre -2 e 5 quais funções usaria?

Resposta
#include <random>

...

std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(-2,5);
distribution(generator); // gera número

Question

E se você quisesse sortear um número real entre 0 e 1?

Resposta
#include <random>

...

std::default_random_engine generator;
std::uniform_real_distribution<double> distribution(0.0, 1.0);
distribution(generator); // gera número

Agora que você já consegue gerar números aleatórios, vamos implementar nossa primeira versão de uma heurística aleatorizada.

Example

Adicionaremos a seguinte variação na nossa heurística: a cada passo de seleção temos 25% de chance de selecionar um objeto aleatório que ainda não foi utilizado. Ou seja, cada passo do algoritmo segue a seguinte regra

  1. Faça um sorteio aleatório
  2. Com probabilidade 75% pegue o próximo objeto não selecionado de acordo com a heurística (mais leve ou mais caro)
  3. Com probabilidade 25% selecione um objeto qualquer dos que não foram analisados ainda.

Note que não mudamos o próximo elemento ao fazer a seleção aleatória. Adote seed=10 nesta tarefa.

Dica: agora é possível que eu olhe um produto mais de uma vez. Você precisará checar isso no seu programa!

Resposta

Os arquivos in-*.txt contém entradas para teste. Os arquivos out-caro-(rand-?)*.txt contém as saídas esperadas para as heurísticas do mais caro. Note que como estamos falando de uma probabilidade, o sorteio deverá ser feito no intervalo [0, 1].

Question

Rode a heurística aleatorizada 10 vezes (como fazer isso?) e anote os valores das mochilas obtidas. Em média, é melhor ou pior que a heuristca sem aleatorização?

Construindo uma solução inteira aleatória

Vamos agora fazer algo mais absurdo: e se criarmos uma solução toda aleatória?

Question

Como você criaria uma solução aleatoriamente?

Resposta

Não existe uma resposta certa aqui. Duas soluções são mais comuns:

  1. passando por cada objeto, pegue-o com probabilidade 50%.
  2. percorra a lista em ordem aleatória, fazendo o mesmo algoritmo do mais caro/leve.

Example

Tente implementar a abordagem 1 da resposta acima.

Example

Tente implementar a abordagem 2 da resposta acima.

Question

Rode ambos programas acima com vários seeds diferentes e anote abaixo os resultados.

Question

Anote aqui comentários sobre a qualidade das soluções aleatórias. Considere tanto o valor dos objetos selecionados quanto o peso.

Warning

Iremos discutir esses resultados na próxima aula.

Algoritmos Genéticos

Nesta aula também abordamos o trade-off entre exploration e exploitation. Para isso, foi possível conhecer uma implementação por algoritmos genéticos para o problema da Mochila Binária em Python. Caso queira executar essa solução, acesse esse link.

Question

Implemente essa solução em C++. Explore as taxas de seleção, cross-over e mutação. Escreva um pequeno relatório (de até 1 página) e comente os principais pontos observados. A entrega desse exercício pode lhe render 0,5 ponto extra, a ser computado ao final da disciplina.