Busca Exponencial em Zig — Implementação e Explicação
A Busca Exponencial é um algoritmo que combina busca exponencial com busca binária. Primeiro, ela encontra um intervalo onde o elemento pode estar, dobrando o índice exponencialmente (1, 2, 4, 8, 16…). Depois, aplica busca binária nesse intervalo. É especialmente útil quando não sabemos o tamanho do array ou quando o elemento está próximo do início.
Como Funciona
- Se o primeiro elemento é o alvo, retorne posição 0
- Encontre o intervalo: comece com i=1 e dobre até
items[i] > alvoou ultrapassar o array - Aplique busca binária no intervalo
[i/2, min(i, n-1)]
Visualização do Processo
Array: [2, 3, 4, 10, 14, 20, 40, 50, 60, 70, 80, 100]
Procurando: 40
Fase 1 — Busca Exponencial (encontrar intervalo):
i=1: items[1]=3 < 40 → continua
i=2: items[2]=4 < 40 → continua
i=4: items[4]=14 < 40 → continua
i=8: items[8]=60 > 40 → intervalo encontrado! [4, 8]
Fase 2 — Busca Binária em [4, 8]:
meio=6: items[6]=40 = 40 ✓
Encontrado na posição 6!
Implementação em Zig
const std = @import("std");
/// Busca exponencial genérica em array ordenado.
/// Retorna o índice do elemento ou null.
pub fn buscaExponencial(comptime T: type, items: []const T, alvo: T) ?usize {
if (items.len == 0) return null;
// Caso especial: primeiro elemento
if (items[0] == alvo) return 0;
// Fase 1: encontrar o intervalo dobrando o índice
var i: usize = 1;
while (i < items.len and items[i] <= alvo) {
i *= 2;
}
// Fase 2: busca binária no intervalo [i/2, min(i, n-1)]
const esquerda = i / 2;
const direita = @min(i, items.len - 1);
return buscaBinaria(T, items, alvo, esquerda, direita);
}
/// Busca binária auxiliar no intervalo [esquerda, direita].
fn buscaBinaria(
comptime T: type,
items: []const T,
alvo: T,
esq: usize,
dir: usize,
) ?usize {
var esquerda = esq;
var direita = dir;
while (esquerda <= direita) {
const meio = esquerda + (direita - esquerda) / 2;
if (items[meio] == alvo) return meio;
if (items[meio] < alvo) {
esquerda = meio + 1;
} else {
if (meio == 0) break;
direita = meio - 1;
}
}
return null;
}
/// Busca exponencial para streams/iteradores onde o tamanho é desconhecido.
/// Usa uma função de acesso que retorna null se o índice for inválido.
pub fn buscaExponencialStream(
comptime T: type,
comptime acessar: fn (usize) ?T,
alvo: T,
) ?usize {
// Verifica o primeiro elemento
if (acessar(0)) |primeiro| {
if (primeiro == alvo) return 0;
} else {
return null;
}
// Encontra o intervalo
var i: usize = 1;
while (acessar(i)) |valor| {
if (valor > alvo) break;
if (valor == alvo) return i;
i *= 2;
}
// Busca binária no intervalo, com verificação de limites
var esquerda = i / 2;
var direita = i;
while (esquerda <= direita) {
const meio = esquerda + (direita - esquerda) / 2;
if (acessar(meio)) |valor| {
if (valor == alvo) return meio;
if (valor < alvo) {
esquerda = meio + 1;
} else {
if (meio == 0) break;
direita = meio - 1;
}
} else {
// Ultrapassou o fim — ajusta direita
if (meio == 0) break;
direita = meio - 1;
}
}
return null;
}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
const dados = [_]i32{ 2, 3, 4, 10, 14, 20, 40, 50, 60, 70, 80, 100 };
const alvos = [_]i32{ 40, 2, 100, 15 };
for (alvos) |alvo| {
if (buscaExponencial(i32, &dados, alvo)) |idx| {
try stdout.print("{d} encontrado na posição {d}\n", .{ alvo, idx });
} else {
try stdout.print("{d} não encontrado\n", .{alvo});
}
}
// Exemplo com stream simulado
const stream_data = [_]i32{ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
const resultado = buscaExponencialStream(i32, struct {
fn f(i: usize) ?i32 {
if (i < stream_data.len) return stream_data[i];
return null;
}
}.f, 13);
if (resultado) |idx| {
try stdout.print("13 no stream: posição {d}\n", .{idx});
}
}
Análise de Complexidade
| Caso | Tempo | Espaço |
|---|---|---|
| Melhor caso | O(1) — elemento no início | O(1) |
| Caso médio | O(log n) | O(1) |
| Pior caso | O(log n) | O(1) |
Detalhamento
- Fase exponencial: O(log i) onde i é a posição do elemento
- Fase binária: O(log i) no intervalo [i/2, i]
- Total: O(log i) que no pior caso é O(log n)
Vantagem para Elementos Próximos ao Início
Se o elemento está na posição i, a busca exponencial faz O(log i) comparações. Se i « n, isso é muito melhor que O(log n) da busca binária!
Quando Usar
- Tamanho desconhecido: arrays ilimitados ou streams
- Elementos frequentemente próximos ao início: acesso proporcional à posição
- Arrays muito grandes: como passo inicial antes da busca binária
Recursos Relacionados
- Busca Binária em Zig — Sub-rotina usada pela busca exponencial
- Busca por Interpolação — Alternativa para dados uniformes
- Busca Linear em Zig — Para dados não ordenados
- Busca Ternária em Zig — Para funções unimodais
- Array Dinâmico — Estrutura com tamanho variável