Você está na página 1de 7

ANÁLISE DE ALGORITMOS

Bacharelado em Ciência da Computação

Lista de Exercícios de revisão (Unidade 2) – Análise de


Algoritmos
Prof. Dr. Gabriel Gadelha

1. Por que backtracking favorece frequentemente melhor eficiência que uma busca exaustiva?
Explique.
Uma das principais vantagens do backtracking é que ele é capaz de evitar a busca exaustiva
em todas as soluções possíveis podando aquelas que não atendem às restrições que devem
ser satisfeitas na solução final e, portanto, não são viáveis, reduzindo assim drasticamente o
espaço de busca.

2. Para encontrar todas as possíveis decodificações da mensagem 10010110101011010, codificada


a partir do esquema a = 10, b = 100, c = 10101, d = 10110, podemos usar um método simples que
inicia à esquerda e tenta decodificar (de todas as formas possíveis) caracter a caracter. Ganha-se
eficiência aplicando uma das técnicas estudadas ao problema. Sendo assim, qual técnica de projeto
de algoritmos deve ser aplicada? Explique como proceder para obter 1 (uma) decodificação válida
da mensagem aplicando uma ou mais restrições, favorecendo assim, a eficiência. Você pode exibir
a árvore de busca para esquematizar sua proposta de solução.
Para resolver o problema de encontrar todas as possíveis decodificações da mensagem codificada,
podemos aplicar a técnica de backtracking para reduzir a busca exaustiva de todas as possibilidades.

Para resolver o problema de encontrar todas as possíveis decodificações da mensagem


codificada, podemos aplicar a técnica de backtracking para reduzir a busca exaustiva de
todas as possibilidades.

O procedimento para obter uma decodificação válida seria:

1. Comece com uma string vazia como a decodificação parcial.


2. Analise o próximo caractere da mensagem codificada e tente todas as possibilidades
de decodificação para esse caractere, verificando se é uma das codificações válidas (a,
b, c ou d).
3. Se uma decodificação válida for encontrada, adicione o caractere correspondente à
decodificação parcial e avance para o próximo caractere da mensagem codificada.
4. Se nenhuma decodificação válida for encontrada, volte um caractere na decodificação
parcial e tente outra possibilidade para esse caractere. Repita até que uma
decodificação válida seja encontrada ou todas as possibilidades tenham sido testadas.
5. Quando toda a mensagem codificada tiver sido decodificada, a decodificação parcial
será uma das decodificações válidas.

1
ANÁLISE DE ALGORITMOS
Bacharelado em Ciência da Computação

Para aumentar a eficiência do algoritmo de backtracking, podemos adicionar restrições,


como por exemplo:

 Se a mensagem codificada começar com um caractere inválido (não corresponde a


nenhuma codificação válida), sabemos que a decodificação parcial atual é inválida e
podemos encerrar a busca para essa ramificação.
 Se a decodificação parcial atual já tem um comprimento maior do que a mensagem
codificada, sabemos que essa
decodificação é inválida e podemos
encerrar a busca para essa
ramificação.

Para exibir a árvore de busca para


esquematizar a solução proposta, podemos
usar um diagrama de árvore, onde cada nó
representa uma decodificação parcial e
cada ramificação representa uma escolha
de decodificação para o próximo caractere
da mensagem codificada. Cada nó pode ser
rotulado com o caractere correspondente a
essa decodificação parcial e cada
ramificação pode ser rotulada com a
codificação usada para essa escolha.

Segue ao lado um exemplo de diagrama de


árvore para a mensagem codificada
"10010110101011010":

3. Existe um algoritmo simples para multiplicar matrizes quadradas de tamanho n (número de


linhas e colunas) cuja complexidade é O(n3) multiplicações. Todavia, determinado cientista propôs
um algoritmo mais eficiente: subdividir as matrizes num produto A x B = C em quatro submatrizes
com a metade do tamanho (e portanto, um quarto de elementos):

Com essa subdivisão, o produto A x B obtém-se pelas equações:

2
ANÁLISE DE ALGORITMOS
Bacharelado em Ciência da Computação

e precisa-se de oito multiplicações de matrizes de tamanho n/2 ao invés de uma multiplicação de


matrizes de tamanho n. A recorrência correspondente, considerando somente multiplicações é:

cuja complexidade é O(n3), que demonstra que essa abordagem não é melhor que o
algoritmo simples (citado no início da questão).
Sendo assim, o cientista inventou as seguintes equações:

(cuja verificação é simples). Essas equações contêm somente sete multiplicações de


tamanho n/2, que leva à recorrência:
T ( n)=7 n
T ( ) O (1)
+2
para o número de multiplicações, cuja complexidade é O(nlg7) = O(n2,81).
Com base no exposto, pode-se considerar que o algoritmo proposto pelo cientista qual das
técnicas de projeto de algoritmos estudadas em sala? Justifique.
Divisão e conquista, pois o cientista iniciou sua proposta de resolução subdividindo seu
problema em problemas menores, em seguida resolveu os problemas menores e, por fim,
compôs a solução para o problema original a partir da resolução dos problemas menores.

4. Por que a recursividade funciona tão bem em divisão-e-conquista, porém não é aplicada em
programação dinâmica? Dica: pense sobre a árvore de recursão gerada para ambas as técnicas!
A recursividade funciona bem em divisão-e-conquista porque essa técnica divide o problema
em subproblemas menores e independentes que podem ser resolvidos recursivamente. Cada
subproblema é resolvido em uma etapa da recursão e seus resultados são combinados para
formar a solução do problema original.

3
ANÁLISE DE ALGORITMOS
Bacharelado em Ciência da Computação

Por outro lado, a programação dinâmica não é geralmente aplicada com recursão direta
porque essa técnica é baseada em memoização, que armazena os resultados dos
subproblemas resolvidos para evitar recalculá-los.

A programação dinâmica usa uma abordagem bottom-up para resolver os subproblemas em


ordem crescente de complexidade, armazenando os resultados em uma tabela. Isso permite
que os resultados dos subproblemas menores sejam usados para resolver subproblemas
maiores, evitando a repetição desnecessária de cálculos.

5. Suponham que o algoritmo A opera sobre uma árvore binária, sem modificá-la, como segue: A
processa o nodo-raiz da árvore em tempo O(1) e, então, recursivamente chama A para as subárvores
esquerda e direita. Pode-se afirmar que A pode ser substancialmente mais eficiente usando
programação dinâmica? Justifique.
Não, a programação dinâmica não pode ser aplicada para tornar o algoritmo A mais eficiente.
A programação dinâmica é uma técnica de otimização que se aplica a problemas de
otimização que possuem subestrutura ótima, ou seja, onde as soluções ótimas dos
subproblemas são usadas para construir a solução ótima do problema original.

No caso do algoritmo A, ele apenas percorre a árvore binária sem modificar seus nodos, e não
envolve a otimização de nenhum problema que possua subestrutura ótima. Portanto, a
aplicação de programação dinâmica não traria nenhum benefício para o algoritmo A, que já
possui uma eficiência assintótica de O(n) (onde n é o número de nodos da árvore), que é a
melhor que se pode esperar para uma operação que percorre todos os nodos de uma árvore.

Assim, não é possível aplicar programação dinâmica para tornar o algoritmo A mais eficiente.
A eficiência do algoritmo A depende principalmente da estrutura da árvore binária e da
distribuição dos nodos, e não de uma técnica específica de programação.

6. Suponha uma série de eventos, por exemplo, as transações feitas na bolsa de valores, na forma
compra Dell, vende HP, compra Google, … Uma certa ação pode acontecer mais de uma vez nessa
sequência. Dada uma outra sequência, decida o mais rápido possível, se ela é uma subsequência da
primeira. O seguinte algoritmo é eficiente para as sequências de tamanho m e n.
algoritmo Subsequencia

Entrada: Sequência S' = s'1...s'm e S = s1...sn

4
ANÁLISE DE ALGORITMOS
Bacharelado em Ciência da Computação

Saída: verdadeiro, se S'  S //S' é uma subsequência de S


se m > n então

retorne falso

i←1

para j ← 1, ..., n

faça se s'i = sj

então
i←i+1

se i>m então retorne verdadeiro

retorne falso
A complexidade do algoritmo é O(n). Sendo assim, é correto afirmar que o algoritmo se baseia em
programação dinâmica? Justifique.
Não, pois se trata do método guloso. Não há, sequer o uso de uma estrutura de dados
adicional que justifique qualquer ideia semelhança com a programação dinâmica.

7. Expliquem a principal diferença entre um algoritmo baseado em PD e um algoritmo guloso para


solucionar um problema.
A principal diferença entre um algoritmo baseado em programação dinâmica e um algoritmo
guloso é a estratégia utilizada para tomar decisões em cada etapa da resolução do
problema.

Em resumo, a principal diferença entre um algoritmo guloso e um algoritmo baseado em


programação dinâmica é que o primeiro faz escolhas locais ótimas a cada etapa, sem levar
em conta as implicações futuras, enquanto o segundo considera todas as escolhas possíveis
e determina a escolha ótima através da comparação de todos os subproblemas possíveis.
Como resultado, a programação dinâmica é geralmente mais precisa e confiável, mas pode
ser mais lenta do que um algoritmo guloso em certas situações.

8. Considere a seguinte fórmula booleana.


(x𝗏y𝗏z)(x𝗏 ¬y)(y𝗏 ¬z)(z𝗏 ¬x)( ¬x𝗏 ¬y𝗏 ¬z)
Qual técnica de projeto de algoritmo pode ser utilizada para resolver o problema da
satisfatibilidade (SAT) dessa fórmula?
Resolva o problema utilizando a técnica proposta e apresente o passo-a-passo.
A técnica de Backtracking pode ser utilizada para resolver o problema da satisfatibilidade
(SAT) dessa fórmula.

Vamos resolver o problema passo a passo:

1. Criar uma lista com as variáveis da fórmula: x, y e z.


5
ANÁLISE DE ALGORITMOS
Bacharelado em Ciência da Computação

2. Atribuir um valor verdadeiro ou falso para a primeira variável, no caso x. Podemos


escolher x = True ou x = False.
3. Avaliar as cláusulas da fórmula que contêm a variável x escolhida no passo 2. Se todas
as cláusulas forem satisfeitas, passamos para a próxima variável da lista (y). Caso
contrário, voltamos para o passo anterior e escolhemos o valor oposto para a variável x.
4. Repetir os passos 2 e 3 para as variáveis y e z, respectivamente.
5. Se chegarmos ao final da lista de variáveis e todas as cláusulas da fórmula forem
satisfeitas, encontramos uma atribuição de valores para as variáveis que satisfaz a
fórmula. Caso contrário, não há solução.

Vamos aplicar essa técnica para a fórmula dada:

1. Lista de variáveis: x, y e z.
2. Escolhemos x = True.
3. Avaliamos as cláusulas que contêm x:
 (x𝗏y𝗏z) é satisfeita para qualquer valor de y e z.
 (x𝗏¬y) é satisfeita para y = False.
 (z𝗏¬x) é satisfeita para z = True.
 Como a cláusula (y𝗏¬z) não é satisfeita para nenhum valor de y e z que respeite
as cláusulas anteriores, voltamos para o passo 2 e escolhemos x = False.
4. Escolhemos y = True.
5. Avaliamos as cláusulas que contêm x e y:
 (x𝗏y𝗏z) é satisfeita para qualquer valor de x, y e z.
 (y𝗏¬z) é satisfeita para z = False.
 (¬x𝗏¬y𝗏¬z) é satisfeita para x = False e z = False.
6. Escolhemos z = True.
7. Avaliamos as cláusulas que contêm x, y e z:
 Todas as cláusulas são satisfeitas.
8. Portanto, encontramos uma atribuição de valores para as variáveis que satisfaz a
fórmula: x = False, y = True e z = True.

Note que a técnica de Backtracking nos permite encontrar uma solução para o problema SAT
da fórmula de forma sistemática, testando todas as possibilidades de atribuição de valores às
variáveis. No entanto, para fórmulas mais complexas, o tempo de execução pode ser muito
grande e outras técnicas, como a Programação Dinâmica, podem ser mais eficientes.

Nessa árvore, cada nó representa uma escolha de valor


para uma das variáveis da fórmula (x, y ou z). Os nós
que levam à solução do problema estão marcados
como "Solution Found". Os nós que levam a um
beacktracking indicam que o valor escolhido para a
variável não leva a uma solução e, portanto,
precisamos voltar atrás e escolher outro valor para uma

6
ANÁLISE DE ALGORITMOS
Bacharelado em Ciência da Computação

variável anterior. Note que a árvore de decisão tem profundidade 3, pois temos 3 variáveis
na fórmula.

9. É dada uma peça retangular de tecido com dimensões X x Y, onde X e Y são inteiros positivos, e
uma lista de n produtos que podem ser feitos usando o tecido. Para cada produto i  [1, n] você
sabe que um retângulo de tecido de dimensões a i x bi é necessário, e que o preço final de venda do
produto é ci. Considere que ai, bi e ci são todos inteiros positivos. Você tem uma máquina que pode
cortar qualquer peça retangular de pano em duas peças, horizontal ou verticalmente. É possível
aplicar programação dinâmica para o projeto de um algoritmo que determine o melhor retorno
possível sobre a peça de tecido de X x Y, ou seja, uma estratégia para cortar o tecido de forma que
os produtos feitos das peças resultantes deem a soma máxima de preços de venda? Justifique.

Você também pode gostar