Aula04
Questões Teóricas Heurísticas e Aleatoriedade¶
Tipo: múltipla escolha (múltiplas corretas)
Enunciado:
Sobre heurísticas com aleatoriedade em problemas de otimização:
a) Uma heurística sempre garante encontrar a solução ótima.
b) O uso de aleatoriedade pode ajudar a escapar de mínimos locais.
c) Estratégias puramente determinísticas podem explorar repetidamente as mesmas regiões do espaço de busca.
d) Uma heurística aleatória nunca pode ser menos eficiente que uma busca determinística.
Ver resposta
a) Incorreta. Heurísticas não garantem a solução ótima, mas sim uma boa solução em tempo razoável.
b) Correta. Técnicas como Algoritmos Genéticos usam aleatoriedade justamente para evitar que a busca fique presa em soluções subótimas.
c) Correta. Sem variação aleatória, a busca pode focar em regiões já visitadas, perdendo diversidade na exploração.
d) Incorreta. O uso de aleatoriedade não garante eficiência. Pode inclusive aumentar o custo (mais iterações, soluções piores) se mal calibrada. A eficiência depende da implementação.
Questão 2 — Busca linear vs aleatória em vetor¶
Enunciado:
Implemente duas funções para encontrar um valor alvo em um vetor:
- Versão linear: percorre o vetor de
0atéN-1. - Versão aleatória: sorteia índices aleatórios até encontrar o alvo (ou até
maxTentativas).
Depois:
- Compile no cluster.
- Crie um script SLURM para rodar ambas versões.
- Compare o tempo e número de tentativas de cada abordagem.
int busca_linear(const std::vector<int>& v, int alvo);
int busca_aleatoria(const std::vector<int>& v, int alvo, unsigned long long maxTentativas);
Ver resposta
busca_linear percorre sequencialmente → excelente localidade espacial.
busca_aleatoria tem acessos aleatórios → pouca localidade, alta variância; pode repetir índices.
Com o vetor v[i]=i, a linear encontra rápido (especialmente se alvo for pequeno); a aleatória pode demorar mesmo com alvo “próximo”.
#include <vector>
#include <random>
// ---------------------------------------------------------
// Função: busca_linear
// Objetivo: procurar o valor 'alvo' de forma sequencial
// Estratégia: percorre o vetor do início ao fim (índices 0..N-1)
// ---------------------------------------------------------
int busca_linear(const std::vector<int>& v, int alvo) {
// Percorre todos os elementos do vetor
for (size_t i = 0; i < v.size(); i++) {
// Se encontrou o alvo, retorna o índice
if (v[i] == alvo) return (int)i;
}
// Se chegou até aqui, o alvo não existe no vetor
return -1;
}
// ---------------------------------------------------------
// Função: busca_aleatoria
// Objetivo: procurar o valor 'alvo' de forma aleatória
// Estratégia: sorteia índices ao acaso até achar o alvo
// ou até atingir o limite de tentativas (maxTentativas).
// ---------------------------------------------------------
int busca_aleatoria(const std::vector<int>& v, int alvo, unsigned long long maxTentativas) {
// Caso o vetor esteja vazio, não há o que buscar
if (v.empty()) return -1;
// Gerador de números aleatórios
std::random_device rd; // fonte de entropia (pode variar a cada execução)
std::mt19937 gen(rd()); // gerador pseudo-aleatório (Mersenne Twister)
std::uniform_int_distribution<size_t> dist(0, v.size() - 1);
// distribuição uniforme de índices válidos [0, N-1]
// Tenta encontrar o alvo até atingir o número máximo de tentativas
for (unsigned long long t = 0; t < maxTentativas; t++) {
size_t idx = dist(gen); // sorteia um índice válido
if (v[idx] == alvo) {
// se encontrou, retorna o índice
return (int)idx;
}
}
// Se não encontrou dentro do limite de tentativas, retorna -1
return -1;
}
Script SLURM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
Questão 3 — Estratégia híbrida (busca sequencial + aleatória)¶
Enunciado:
Implemente uma função que:
- Primeiro verifica os K primeiros elementos do vetor sequencialmente.
- Se não encontrar, passa a buscar usando índices aleatórios até maxTentativas.
Depois:
- Compile e rode no cluster com SLURM.
- Compare os resultados com as funções da Questão 2.
int busca_hibrida(const std::vector<int>& v, int alvo, int K, unsigned long long maxTentativas);
Ver resposta
#include <vector>
#include <random>
// ---------------------------------------------------------
// Função: busca_hibrida
// Objetivo: combinar duas estratégias de busca:
// (1) varre sequencialmente os K primeiros elementos (bom p/ localidade)
// (2) se não achar, usa tentativas aleatórias até maxTentativas
//
// Quando é útil?
// - Se o alvo tem maior probabilidade de estar no início do vetor,
// a parte sequencial encontra rápido.
// - Caso contrário, a fase aleatória pode "acertar" em elementos distantes
// sem percorrer todo o vetor sequencialmente.
// ---------------------------------------------------------
int busca_hibrida(const std::vector<int>& v, int alvo, int K, unsigned long long maxTentativas) {
// -------- Parte 1: busca sequencial nos primeiros K elementos --------
// Varre de 0 até K-1 (limitando também pelo tamanho do vetor).
// Vantagem: acesso contíguo → melhor localidade de memória.
for (int i = 0; i < K && i < (int)v.size(); i++) {
if (v[i] == alvo) return i; // achou no prefixo
}
// -------- Parte 2: busca aleatória no vetor inteiro --------
// Observação: esta versão sorteia em [0, N-1]. Em muitos casos,
// restringir a [K, N-1] faz mais sentido (evita repetir o prefixo já checado).
if (v.empty()) return -1; // vetor vazio → não há o que buscar
// Geradores para sorteio de índices válidos
std::random_device rd; // fonte de entropia (não determinística)
std::mt19937 gen(rd()); // PRNG (Mersenne Twister)
std::uniform_int_distribution<size_t> dist(0, v.size() - 1); // sorteia 0..N-1
// Tenta até atingir o limite de tentativas aleatórias
for (unsigned long long t = 0; t < maxTentativas; t++) {
size_t idx = dist(gen); // sorteia um índice
if (v[idx] == alvo) { // compara com o alvo
return (int)idx; // achou durante a fase aleatória
}
}
// Não encontrou nem na parte sequencial, nem na aleatória
return -1;
}
Questão 4 — Heurística com pré-filtro¶
Enunciado:
Implemente uma função de busca que usa uma heurística simples de pré-filtro:
- Antes de acessar v[i], só considere o índice se i % 2 == 0 (ou seja, só olha posições pares).
- Se não encontrar após maxTentativas, faça busca linear completa como fallback.
Depois:
- Compile e rode no cluster com SLURM.
- Compare desempenho e número de acessos com as versões anteriores.
int busca_com_filtro(const std::vector<int>& v, int alvo, unsigned long long maxTentativas);
Ver resposta
A busca aleatória é restrita a índices pares, tentando reduzir acessos.
Caso não encontre dentro de maxTentativas, entra uma busca linear
O pré-filtro pode ser vantajoso se há maior probabilidade de o alvo estar em posições pares.
Porém, pode desperdiçar tentativas (índices ímpares descartados) e no pior caso cair na busca linear.
#include <vector>
#include <random>
int busca_com_filtro(const std::vector<int>& v, int alvo, unsigned long long maxTentativas) {
if (v.empty()) return -1;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<size_t> dist(0, v.size() - 1);
// Heurística: só verifica índices pares
for (unsigned long long t = 0; t < maxTentativas; t++) {
size_t idx = dist(gen);
if (idx % 2 != 0) continue; // ignora índices ímpares
if (v[idx] == alvo) return (int)idx;
}
// Fallback: se não achou, faz busca linear completa
for (size_t i = 0; i < v.size(); i++) {
if (v[i] == alvo) return (int)i;
}
return -1; // não encontrado
}