Algoritmos e Estruturas de DadosFundamentos
O Papel dos Algoritmos na Computação
17 min de leitura · 13ª mais cobrada em Algoritmos e Estruturas de Dados
O que é um algoritmo
Desenvolver um algoritmo é escolher um procedimento — uma sequência de decisões que um agente sem criatividade consegue executar.
Um algoritmo é uma sequência finita de passos bem definidos que, dada uma entrada válida, produz uma saída e termina. Cada passo precisa ser executável: não há "e então faça o óbvio". Linguagem natural, fluxograma ou pseudocódigo são só escritas do mesmo objeto.
Formalmente, um algoritmo associa cada entrada de um conjunto a uma saída no conjunto das respostas aceitáveis. "Somar de a " e "encontrar o maior de números" são problemas; o laço que acumula, a varredura que atualiza o máximo — isso é o algoritmo.
A definição impõe três condições ao mesmo tempo:
- Finitude e término. Uma receita que pode girar para sempre não é algoritmo: o texto dos passos é finito e a execução para.
- Passos claros. "Escolha um número bom" não é passo. "Compare com e, se for maior, substitua " é.
- Correção. Sem a garantia de que responde o problema para toda válida, o que se tem é um processo que às vezes acerta.
Por exemplo, máximo de : ; compare com (fica ); compare com (fica ). Três comparações, termina, devolve . Já é um algoritmo.
Correção e custo
Dois juízos distintos, nesta ordem:
- Correção. Para toda entrada válida, a saída é uma resposta do problema. Sem isso não há algoritmo útil.
- Custo (eficiência). Quanto tempo e memória o procedimento consome em função do tamanho da entrada. Dois algoritmos corretos para o mesmo problema podem diferir por ordens de grandeza.
A correção pergunta "sempre dá a resposta certa?". O custo pergunta "dá para esperar o fim quando cresce?". Uma varredura linear e uma busca que corta o vetor já ordenado ao meio a cada passo encontram um elemento, se ele existir; a segunda faz bem menos comparações.
Para medir custo define-se uma função de :
- Se mede o trabalho para executar o algoritmo numa entrada de tamanho , é função de complexidade de tempo.
- Se mede a memória extra (além da entrada) usada na execução, é função de complexidade de espaço.
A função estima quantas vezes uma operação relevante — comparação, atribuição, acesso — é executada. A mesma descreve o algoritmo em qualquer processador; o cronômetro da máquina mistura processador, linguagem e carga do sistema.
A análise de pior caso estabelece um limite superior para o tempo que vale para qualquer entrada de tamanho — o limite "nunca pior que isto". Melhor caso e caso médio existem; o que segura todas as entradas é o de pior caso.
Quando o procedimento é recursivo — chama a si mesmo em entradas menores —, o tempo não se lê num único laço: descreve-se por uma relação de recorrência , equação que define o custo a partir de nos subproblemas, mais o trabalho de combinar. No iterativo, o custo é a soma de laços; no recursivo, essa recorrência.
Um algoritmo pode ser correto e mesmo assim inútil na prática: enumerar as permutações de um vetor — as ordens possíveis — e devolver a ordenada está correto e custa um fatorial só para listá-las. É a escolha da família de projeto que muda essa ordem de grandeza.
Correção não se prova no relógio. Três vetores de teste não substituem uma prova para toda entrada válida. Custo não se prova só com um pequeno: constantes escondidas enganam o cronômetro.
Cinco famílias de projeto
Antes de escrever laços, escolhe-se como o problema será atacado. Cada linha da tabela é uma família, não um algoritmo pronto.
| Estratégia | Ideia | Exemplo mínimo |
|---|---|---|
| Tentativa e erro | Parte o trabalho em subtarefas finitas e explora cada ramo até acertar ou esgotar. | Abrir um cadeado de 3 dígitos: testar , , … até a combinação. |
| Dividir e conquistar | Parte em instâncias menores, resolve cada uma, combina. | Ordenar as duas metades e depois intercalar. |
| Guloso | Constrói a solução em etapas; em cada etapa pega o melhor item viável e não volta atrás. | Troco com moedas : sempre a maior que ainda cabe. |
| Aproximado | Abre mão do ótimo; garante que o resultado fica numa razão conhecida do ótimo. | Cobertura de vértices -aproximada: o custo é no máximo o dobro do mínimo. |
| Heurística | Pode dar um bom resultado — às vezes o ótimo — mas não garante qualidade nem que ache solução. | Subir sempre para o vizinho de maior valor: pode parar antes do máximo. |
O guloso, quando vem com prova (escolha gulosa), devolve ótimo. A heurística é um palpite organizado: pode falhar. O aproximado admite erro, mas o erro tem teto — uma razão . Tentativa e erro percorre o espaço; backtracking é a versão que poda ramos já inviáveis.
Tentativa e erro
O espaço de candidatos é finito. O procedimento parte o trabalho em subtarefas — um dígito do cadeado, uma rainha numa linha, um item entra-ou-não no subconjunto — e explora cada ramo até achar resposta ou esgotar. Sem poda, isso é pesquisa exaustiva: ou , conforme o objeto. Com poda (um ramo já viola a restrição), o mesmo desenho vira backtracking.
Dividir e conquistar
O problema de tamanho vira dois (ou ) problemas de tamanho , mais um custo de combinação. Ordenar as metades e intercalar é o caso-guia: depois das partes, junta — diferente de "escolher o melhor item e seguir". O custo desse desenho é uma recorrência.
Guloso
Constrói a solução em etapas. Em cada etapa seleciona o melhor elemento da entrada que ainda é viável (cabe no troco, não sobrepõe o último intervalo) e não desfaz a escolha. Se houver prova de que essa escolha local leva ao ótimo global, o algoritmo é guloso no sentido forte. Troco com moedas (sempre a maior que cabe) é o exemplo mínimo — e neste sistema de moedas acerta.
Aproximado
Abre mão do ótimo. O que resta é uma razão: se é o valor ótimo e o valor produzido, garante-se (minimização) ou (maximização) para um fixo. Uma cobertura de vértices é um conjunto de vértices que toca toda aresta. O procedimento que pega um emparelhamento maximal (conjunto de arestas sem vértice em comum, impossível de aumentar) e devolve os dois extremos de cada aresta escolhida é -aproximado: cada aresta do emparelhamento exige pelo menos um vértice em qualquer cobertura ótima, e o algoritmo cobra dois, logo . Sem esse teto, o procedimento cai na classe heurística.
Heurística
Pode produzir um bom resultado, às vezes o ótimo, e pode devolver saída sem garantia ou nenhuma solução. Não há prova de otimalidade nem razão . Subir sempre ao vizinho de maior valor (subida de encosta) para num pico local: um ponto maior que os vizinhos imediatos, menor que o máximo global. Um "guloso" sem prova é, nesta classificação, heurística: a regra local é a mesma, a garantia é que some.
Balanceamento: partes de tamanho semelhante
Vetor de tamanho . Recortar esses itens custa : no máximo uma constante vezes quando é grande.
A primeira chamada separa item e recorre em , pagando no recorte.
A chamada de tamanho separa e recorre em , pagando . Continua até .
A árvore de recursão — o desenho das subchamadas, um nível por recorte — vira um caminho. A recorrência é ; somando, ( é a mesma ordem de grandeza).
A chamada de tamanho parte em duas de tamanho e paga .
Cada parte em duas de tamanho ; cada parte em duas de tamanho . Há níveis, e em cada nível as partes somam , logo cada nível custa . Total .
Um recorte é balanceado quando os subproblemas têm tamanho da mesma ordem — duas metades , ou fatias de cerca de . Recortar em e também produz partes menores, mas os tamanhos não são semelhantes: isso não é balanceamento. Balanceamento é a decomposição em partes menores de tamanho semelhante.
Com partes semelhantes e trabalho de recorte ,
A árvore tem níveis e cada nível soma .
Com o recorte torto,
A árvore vira um caminho de níveis; o total é .
O termo é o mesmo; o que muda a classe é o tamanho das partes.
O mapa das cinco famílias classifica estratégia de ataque. Em dividir e conquistar o problema parte, resolve-se cada peça e junta-se numa solução global — esse é o combinar. Balanceamento exige só a subdivisão equilibrada. Pode-se partir ao meio sem fase de combinação (ainda é balanceamento) e pode-se combinar depois de um recorte e (é combinar, não é balanceamento).
Exemplo: nomear o desenho
O mesmo enunciado admite mais de uma família. O mapa serve para nomear o desenho. Considere o problema: dado um conjunto de intervalos na reta, escolher o maior subconjunto em que nenhum par se sobrepõe. Quatro intervalos, já escritos como :
Há subconjuntos. Para cada um, teste se algum par se cruza; entre os viáveis, fique com o de maior cardinalidade. Isso decompõe o processo em subtarefas finitas (incluir ou não cada intervalo) e as explora exaustivamente. Um ramo que já juntou e pode ser podado — aí o mesmo desenho vira backtracking — mas sem poda o espaço inteiro é visitado. Resposta: ou , ambos de tamanho .
Recorte os quatro intervalos ao meio. Esquerda: e . Direita: e . Primeiro vêm as partes; depois, a junta.
Na esquerda os dois se cruzam: e estão dentro de . O maior subconjunto sem sobreposição tem um intervalo. Na direita, e se cruzam: outro subconjunto de tamanho .
A solução global é a melhor entre (i) a da esquerda, (ii) a da direita e (iii) a melhor união viável que cruza o corte. Só (i) e (ii) dariam tamanho , e o enunciado pede o maior. termina em e começa em : não se cruzam, tamanho . termina em e começa em : não invade o interior, tamanho . Sem essa junta, o recorte ao meio ainda não é esta família.
Ordene por instante de término e, da esquerda para a direita, aceite o próximo intervalo que não sobrepõe o último aceito. Comece vazio.
- termina primeiro e é viável aceita. Último fim .
- sobrepõe recusa.
- começa em , não invade o interior de aceita. Último fim .
- sobrepõe recusa.
Solução , tamanho . Cada passo pegou o melhor item ainda viável e não voltou atrás. Neste problema a escolha gulosa tem prova. O desenho devolve ótimo.
Aproximado pediria um teto: "o tamanho devolvido é pelo menos " para um fixo, mesmo sem acertar o ótimo. Uma regra do tipo "aceite o intervalo mais curto que ainda cabe, sem prova nem " classifica-se como heurística.
Heurística também é subir ao vizinho (trocar um intervalo da solução por outro). Comece com : trocar por mantém tamanho , pico local. Comece com e acrescente : chega , tamanho . Pode acertar, pode parar antes.
O critério que separa as famílias é a garantia. Guloso com prova devolve ótimo. Aproximado admite erro com razão. Heurística pode falhar — devolver saída sem garantia ou nenhuma solução.
Não misture as cinco frases-guia:
- "Subtarefas finitas exploradas exaustivamente" tentativa e erro.
- "Divide, resolve as partes, combina" dividir e conquistar.
- "Em cada etapa o melhor elemento viável, sem desfazer" guloso.
- "O resultado fica numa razão conhecida do ótimo" aproximado.
- "Pode acertar, pode devolver saída sem garantia ou nenhuma solução" heurística.
Metodologia em quatro perguntas
A metodologia de desenvolvimento é o mapa das cinco famílias mais as quatro perguntas abaixo.
- O que entra e o que sai? Conjunto de intervalos um subconjunto de cardinalidade máxima sem sobreposição.
- Qual família cabe? Exaustão é tentativa e erro; recortar e costurar é dividir e conquistar; término mais cedo com prova é guloso; "primeiro que parecer bom" sem prova é heurística.
- O procedimento está correto? Indução — prova que avança de um caso base a — ou checagem exaustiva nos casos pequenos. Um algoritmo errado e rápido não vira correto por ser barato.
- Quanto custa? Melhor / pior / médio em função de , contando a operação relevante. Recorrência no recursivo, soma de laços no iterativo.
Não pule a pergunta . Um procedimento que "escolhe intervalos" sem dizer se o critério é cardinalidade, comprimento total ou estabilidade ainda não tem problema. Não misture e .
Em Resumo
Algoritmo: passos finitos, claros e terminantes que levam entrada válida a saída correta.
Correção (sempre a resposta certa) é independente de custo (tempo e espaço em ).
Tempo conta operação relevante, não o relógio; espaço conta memória extra; pior caso é o limite superior que vale para qualquer entrada.
Recursivo: o custo descreve-se por uma recorrência, não por um único laço.
Cinco famílias: tentativa e erro, dividir e conquistar, guloso, aproximado (razão) e heurística (pode falhar); no guloso há prova de otimalidade.
Balanceamento: decompor em partes menores de tamanho semelhante — versus — distinto do combinar da divisão e conquista.