Lista de exercícios
Exercícios resolvidos: complexidade de algoritmos
Lista progressiva e comentada sobre contagem de instruções, crescimento assintótico e análise de recorrências.
Esta lista pratica os conceitos da Aula 01 em uma sequência progressiva. Tente resolver cada exercício antes de consultar a solução. O tempo indicado nos metadados é apenas uma estimativa de estudo, não um limite.
Cada exercício é seguido pela respectiva solução. A resolução registra as premissas, os cálculos intermediários e a conclusão.
Exercício 1: contagem exata com execução condicional
Analise o fragmento abaixo:
long sum_even(const int values[], size_t count) {
long total = 0L;
for (size_t index = 0U; index < count; index++) {
if (values[index] % 2 == 0) {
total += values[index];
}
}
return total;
}
Considere uma unidade para cada:
- inicialização de
total; - inicialização de
index; - teste
index < count; - incremento
index++; - teste que verifica se o valor é par;
- atualização de
total; - retorno da função.
Seja e seja a quantidade de valores pares.
- Escreva .
- Calcule o melhor caso.
- Calcule o pior caso.
- Calcule .
- Classifique o melhor e o pior caso com .
Solução
Explicação
Identificação das variáveis
O tamanho da entrada é , pois o arranjo possui count elementos.
A variável informa quantas vezes o corpo do if executa. Logo:
Trace do laço
Considere count = 4. Cada linha abaixo representa uma avaliação de index < count:
| Avaliação | index antes do teste | Teste | Resultado | O que acontece depois |
|---|---|---|---|---|
| 1 | 0 | 0 < 4 | verdadeiro | O corpo executa e index++ produz 1. |
| 2 | 1 | 1 < 4 | verdadeiro | O corpo executa e index++ produz 2. |
| 3 | 2 | 2 < 4 | verdadeiro | O corpo executa e index++ produz 3. |
| 4 | 3 | 3 < 4 | verdadeiro | O corpo executa e index++ produz 4. |
| 5 | 4 | 4 < 4 | falso | O laço termina. O corpo e o incremento não executam. |
Os quatro elementos produzem quatro testes verdadeiros. O laço precisa de mais um teste, agora falso, para descobrir que deve terminar. Portanto, para elementos, index < count é avaliado vezes. O corpo e index++ executam somente vezes.
Contagem de cada operação
total = 0L 1 vez
index = 0U 1 vez
index < count n + 1 vezes
index++ n vezes
values[index] % 2 == 0 n vezes
total += values[index] e vezes
return total 1 vez
Construção de
Somando as quantidades:
Agrupando os termos:
Melhor e pior caso
O melhor caso ocorre quando nenhum valor é par. Nesse caso, :
O termo dominante é . Portanto:
O pior caso ocorre quando todos os valores são pares. Nesse caso, :
O termo dominante continua sendo . Portanto:
Cálculo para e
Resposta esperada
- .
- No melhor caso, e .
- No pior caso, e .
- .
- O melhor e o pior caso pertencem a .
Exercício 2: quantidade de pares processados
Analise o fragmento abaixo. Considere que cada chamada de process possui custo constante e não usa espaço auxiliar que cresça com .
void process_pairs(const int values[], size_t count) {
for (size_t left = 0U; left < count; left++) {
for (size_t right = 0U; right < left; right++) {
process(values[left], values[right]);
}
}
}
Conte somente as chamadas de process.
- Quantas chamadas ocorrem para cada valor de
left? - Escreva a soma que representa todas as chamadas.
- Obtenha uma fórmula fechada.
- Calcule .
- Classifique o tempo e o espaço auxiliar.
Solução
Explicação
O que deve ser contado
O enunciado pede somente as chamadas de process. Uma chamada ocorre para cada par (left, right) em que o laço alcança o corpo, isto é, quando right < left é verdadeiro.
Trace em forma de matriz
Considere . As linhas representam left e as colunas representam right. O símbolo X marca uma chamada de process. O símbolo · indica que process não é chamado naquela combinação. Ele não significa que a condição foi necessariamente avaliada.
left / right | 0 | 1 | 2 | 3 | 4 | Chamadas |
|---|---|---|---|---|---|---|
| 0 | · | · | · | · | · | 0 |
| 1 | X | · | · | · | · | 1 |
| 2 | X | X | · | · | · | 2 |
| 3 | X | X | X | · | · | 3 |
| 4 | X | X | X | X | · | 4 |
Os X formam o triângulo estritamente inferior da matriz, abaixo da diagonal em que right = left. Na linha left, existem exatamente left símbolos X e, portanto, left chamadas de process.
Soma das chamadas
O termo zero não altera a soma. Essa é a soma dos inteiros de até , cuja fórmula é:
Expandindo a expressão:
Chamadas de process e avaliações da condição não são a mesma contagem
Para cada valor de left, o teste right < left ocorre left vezes com resultado verdadeiro e mais uma vez com resultado falso. Esse último teste falso encerra o laço interno. Assim, a quantidade total de avaliações da condição é:
Esse valor não responde ao enunciado, pois a avaliação falsa não chama process. A contagem solicitada inclui somente os X da matriz, isto é, chamadas.
Cálculo para
Classificação do tempo e do espaço
O termo dominante de é . A constante e o termo não alteram a classe assintótica:
O fragmento usa apenas os índices left e right, além dos parâmetros. A quantidade de variáveis não cresce com :
Resposta esperada
- Para cada valor de
left, ocorremleftchamadas deprocess. - .
- .
- .
- O tempo pertence a e o espaço auxiliar pertence a .
Exercício 3: valores concretos e taxas de crescimento
Considere três custos:
- Calcule os três custos para .
- Ordene os algoritmos do menor para o maior custo nesse valor de .
- Determine a classe de cada função.
- Ordene as classes pela taxa de crescimento.
- Para , escreva limites , e usando a ordem dominante.
Solução
1. Cálculo de
2. Cálculo de
Como , temos :
3. Cálculo de
Para , a ordem concreta é:
4. Classes assintóticas
Em , o termo dominante é :
Em , o termo dominante é :
Em , o termo dominante é :
As taxas de crescimento ficam nesta ordem:
O custo quadrático foi o menor para por causa dos coeficientes das funções. Isso não altera o fato de que cresce mais rapidamente quando aumenta.
5. Limites de
Em , o termo dominante é . Usando a mesma ordem dominante nos três limites:
Resposta: para , os custos são , e . Assintoticamente, as classes são , e .
Exercício 4: modelagem de recorrências
Escreva o caso base e a recorrência de cada procedimento. Use como caso base.
Procedimento A
- cria duas chamadas com entradas de tamanho ;
- após as chamadas, percorre todos os elementos três vezes.
Procedimento B
- cria uma chamada com entrada de tamanho ;
- após a chamada, executa duas operações constantes.
Procedimento C
- cria quatro chamadas com entradas de tamanho ;
- após as chamadas, executa operações locais.
Para cada procedimento:
- identifique o custo das chamadas recursivas;
- identifique o trabalho local;
- escreva a recorrência completa.
Solução
Procedimento A
Duas chamadas com tamanho produzem:
Três percursos completos executam operações. Como o fator é constante:
A recorrência pode preservar o coeficiente exato:
Na forma assintótica do trabalho local:
Procedimento B
Uma chamada com tamanho produz:
Duas operações formam um custo constante:
Logo:
ou:
Procedimento C
Quatro chamadas com tamanho produzem:
O enunciado informa trabalho local quadrático. Portanto:
ou:
Resposta: os termos recursivos representam a quantidade e o tamanho das chamadas. O termo após a soma representa somente o trabalho local da chamada atual.
Exercício 5: resolução pelo método iterativo
Considere como uma potência de :
- Faça três expansões.
- Escreva a expressão após expansões.
- Determine por meio do caso base.
- Obtenha a fórmula exata de .
- Classifique a função com .
- Calcule .
Solução
1. Três primeiras expansões
A primeira aplicação da recorrência é:
Na segunda aplicação, use a mesma regra com entrada :
Substituindo na expressão original:
Na terceira aplicação:
Logo:
Cada aplicação divide o tamanho por e acrescenta .
2. Padrão após expansões
3. Quantidade de expansões
A expansão termina quando a entrada chega a :
Multiplicando os dois lados por :
Aplicando logaritmo de base :
4. Fórmula final
Substituindo no padrão:
Como :
O termo dominante é logarítmico:
5. Cálculo para
Como :
Resposta: e .
Exercício 6: verificação pelo método de substituição
Considere:
Use substituição e indução para verificar a proposta:
A resolução deve conter:
- verificação do caso base;
- hipótese para ;
- substituição da hipótese na recorrência;
- conclusão exata e assintótica.
Solução
1. Caso base
A recorrência informa:
A proposta para fornece:
O valor proposto coincide com o caso base.
2. Hipótese de indução
Suponha que a proposta seja válida para :
Simplificando:
3. Substituição na recorrência
A recorrência é:
Substituindo a hipótese :
O resultado coincide com a proposta.
4. Conclusão
O caso base é válido e a hipótese para produz a expressão para . Portanto:
O termo dominante é :
Resposta: a proposta foi verificada e pertence a .
Exercício 7: árvore de recursão com níveis equilibrados
Considere como uma potência de :
- Construa os níveis para .
- Informe a quantidade de nós, o tamanho da entrada e o custo local total de cada nível.
- Calcule a quantidade e o custo total das folhas.
- Calcule .
- Generalize o custo do nível .
- Obtenha a classe assintótica.
Solução
1. Interpretação da recorrência
Em uma chamada com entrada de tamanho :
- o termo cria duas chamadas com metade da entrada;
- o termo representa o trabalho local da chamada atual;
- uma folha custa .
2. Níveis para
Nível 0: 1 nó de tamanho 16 custo por nó: 32 custo do nível: 32
Nível 1: 2 nós de tamanho 8 custo por nó: 16 custo do nível: 32
Nível 2: 4 nós de tamanho 4 custo por nó: 8 custo do nível: 32
Nível 3: 8 nós de tamanho 2 custo por nó: 4 custo do nível: 32
Nível 4: 16 folhas de tamanho 1 custo por folha: 2 custo do nível: 32
Existem quatro níveis internos, numerados de a . O nível contém os casos base.
3. Custo exato para
Os quatro níveis internos custam cada:
As folhas custam cada:
Somando:
4. Generalização do nível
No nível interno :
O custo local de um nó com entrada é:
Multiplicando a quantidade de nós pelo custo de cada nó:
Todos os níveis internos custam .
5. Altura e custo total
A árvore alcança o caso base quando:
Logo:
Existem níveis internos. O custo interno é:
Existem folhas, cada uma com custo . O custo das folhas é :
Portanto:
Resposta: . Cada nível interno custa , e o custo total pertence a .
Exercício 8: árvore de recursão dominada pelas folhas
Considere como uma potência de :
- Construa os níveis para .
- Calcule o custo local total de cada nível interno.
- Calcule a quantidade de folhas.
- Calcule .
- Generalize o custo do nível .
- Explique por que o resultado não recebe um fator adicional .
Solução
1. Interpretação da recorrência
Cada chamada com entrada :
- cria quatro chamadas com entrada ;
- executa unidades de trabalho local;
- termina com custo quando .
2. Níveis para
Nível 0: 1 nó de tamanho 8 custo por nó: 16 custo do nível: 16
Nível 1: 4 nós de tamanho 4 custo por nó: 8 custo do nível: 32
Nível 2: 16 nós de tamanho 2 custo por nó: 4 custo do nível: 64
Nível 3: 64 folhas de tamanho 1 custo por folha: 1 custo do nível: 64
3. Custo exato para
4. Generalização do custo interno
No nível interno :
O custo total do nível é:
Como :
O custo dobra a cada descida na árvore.
5. Quantidade de folhas
A altura satisfaz:
Como a quantidade de nós é multiplicada por em cada nível:
Substituindo :
As folhas custam cada. Logo, o último nível custa .
6. Custo assintótico
Os custos aumentam geometricamente em direção às folhas. Vistos de baixo para cima, os níveis anteriores formam frações sucessivas do custo dominante:
A soma dos níveis anteriores é menor que outro :
Portanto:
O fator não aparece porque os níveis não possuem o mesmo custo. O último nível domina a soma.
Resposta: , existem folhas e .
Exercício 9: método mestre com domínio das folhas
Considere:
- Identifique , e .
- Calcule .
- Compare com .
- Identifique o caso do método mestre.
- Obtenha a classe assintótica.
Use o mapa abaixo para organizar a comparação entre e antes de abrir a solução.
flowchart TD
A["Identificar a, b e f(n)"] --> B["Calcular n elevado a log_b(a)"]
B --> C{"Comparar f(n)"}
C -->|Menor por fator polinomial| D["Caso 1"]
C -->|Mesma ordem assintótica| E["Caso 2"]
C -->|Maior por fator polinomial| F["Verificar regularidade"]
F -->|Condição satisfeita| G["Caso 3"]
Solução
1. Parâmetros
A forma do método mestre é:
Comparando com a recorrência do exercício:
a = 8 porque existem oito subproblemas
b = 2 porque cada subproblema recebe n/2
f(n) = n² porque esse é o trabalho local
2. Expoente de referência
Portanto:
3. Comparação dos expoentes
O trabalho local é:
O expoente local é , enquanto o expoente de referência é :
O trabalho local possui uma potência menor. Portanto, as folhas dominam e o caso 1 se aplica.
4. Resultado
No caso 1:
Substituindo :
Resposta: , , , , o caso 1 se aplica e .
Exercício 10: método mestre com níveis equilibrados e domínio da raiz
Resolva as duas recorrências com o método mestre.
Recorrência A
Recorrência B
Para cada recorrência:
- identifique , , e ;
- compare o expoente de com ;
- identifique o caso;
- verifique a regularidade quando necessária;
- obtenha a classe assintótica.
Solução
Recorrência A: identificação dos parâmetros
a = 9
b = 3
f(n) = n²
O expoente de referência é:
Logo:
O trabalho local também é . Portanto, o expoente local é igual a :
Os níveis são equilibrados e o caso 2 se aplica:
Substituindo :
Recorrência B: identificação dos parâmetros
a = 2
b = 2
f(n) = n³
O expoente de referência é:
Logo:
O trabalho local possui expoente :
Antes de usar o caso 3, é necessário verificar a regularidade:
Substituindo , e :
Como :
Podemos usar , que é menor que . A regularidade é satisfeita.
No caso 3, o trabalho local domina:
Resposta: a recorrência A usa o caso 2 e resulta em . A recorrência B usa o caso 3, satisfaz a regularidade com e resulta em .
Exercício 11: decisão sobre a aplicação do método mestre
Para cada recorrência, informe se os três casos do método mestre apresentados na aula podem ser usados. Quando o método puder ser usado, determine a classe assintótica. Quando não puder, indique a condição que falha.
Solução
Recorrência A
O método exige subproblemas com tamanho , em que é constante.
Na recorrência A, a entrada diminui por subtração:
Não existe uma divisão por um fator constante . Portanto, a recorrência não possui a forma exigida.
Decisão: rejeitar o método mestre apresentado na aula.
Recorrência B
O método exige que todos os subproblemas tenham o mesmo tamanho .
Nesta recorrência, os tamanhos são diferentes:
e:
Não é possível representar as duas chamadas por um único termo .
Decisão: rejeitar o método mestre apresentado na aula.
Recorrência C
Os parâmetros iniciais são:
a = 4
b = 2
p = log₂ 4 = 2
nᵖ = n²
f(n) = n² / log n
O trabalho local é menor que , mas a diferença ocorre somente pelo fator logarítmico . O caso 1 apresentado na aula exige uma diferença por potência:
para alguma constante .
O fator não fornece essa diferença de potência. O caso 2 também não serve porque não pertence a .
Decisão: rejeitar os três casos apresentados na aula. Uma versão ampliada do método pode tratar essa recorrência, mas ela não faz parte do conteúdo utilizado nesta lista.
Recorrência D
Os parâmetros são:
a = 2
b = 2
f(n) = n²
p = log₂ 2 = 1
O expoente local é e o expoente de referência é :
É necessário verificar a regularidade:
Existe . Portanto, a regularidade é satisfeita e o caso 3 se aplica:
Decisão: usar o caso 3. O resultado é .
Resposta: rejeitar o método nas recorrências A, B e C pelos motivos indicados. Na recorrência D, usar o caso 3 e obter .
Exercício 12: decisão de engenharia com tempo e memória
Um sistema precisa ordenar registros por timestamp. Cada registro ocupa bytes. Registros com o mesmo timestamp devem preservar a ordem original.
Considere duas alternativas:
- inserção: até comparações, espaço auxiliar e ordenação estável;
- intercalação: trabalho proporcional a , buffer auxiliar para registros e ordenação estável.
O sistema possui MiB disponíveis para o buffer. Use bytes.
- Calcule o pior caso de comparações da inserção.
- Calcule aproximadamente. Use .
- Calcule o buffer da intercalação em bytes e MiB.
- Verifique as restrições de estabilidade e memória.
- Escolha a alternativa e justifique com os valores calculados.
Solução
1. Pior caso da inserção
Substituindo :
Primeiro, divida por :
O pior caso exige quase vinte bilhões de comparações no modelo fornecido.
2. Escala da intercalação
Esse valor representa a escala informada no enunciado. Ele não é uma contagem exata de todas as instruções da implementação.
3. Tamanho do buffer
Cada um dos registros ocupa bytes:
Convertendo para MiB:
Como , o buffer cabe na memória disponível.
4. Verificação das restrições
Estabilidade da inserção: atendida
Estabilidade da intercalação: atendida
Memória da inserção: atendida
Memória da intercalação: 9,16 MiB < 12 MiB, atendida
As duas alternativas atendem à estabilidade. A intercalação também atende ao limite de memória.
5. Decisão
A comparação de escala fornece:
Inserção no pior caso: 19 999 900 000 comparações
Intercalação: 3 522 000 unidades proporcionais
Buffer da intercalação: 9,16 MiB
Memória disponível: 12 MiB
A intercalação possui crescimento , atende à estabilidade e seu buffer cabe na memória. A inserção economiza memória, mas seu pior caso quadrático é muito maior para a entrada fornecida.
Resposta: escolher a intercalação, considerando somente o modelo e as restrições declaradas no exercício.