Subsequência comum mais longa Em inglês, Longest Common Subsequence (LCS)



Documentos relacionados
Fernando Lobo. Algoritmos e Estrutura de Dados. Outra técnica de concepção de algoritmos, tal como Divisão e Conquista.

Comparação com Divisão e Conquista

Disciplina de Projetos e Análise de Algoritmos

Análise e Síntese de Algoritmos. Programação Dinâmica CLRS, Cap. 15

PROGRAMAÇÃO DINÂMICA

Divisão e Conquista: Par de Pontos mais Próximo

Análise de Problemas Recursivos. Algoritmos e Estruturas de Dados Flavio Figueiredo (

Análise e Complexidade de Algoritmos

Programação Dinâmica. Programa do PA. Técnicas Avançadas de Projeto. Aulas Anteriores. Introdução. Plano de Aula. Técnicas de Projeto de Algoritmos

Programação Dinâmica. Prof. Anderson Almeida Ferreira. Adaptado do material elaborado por Andrea Iabrudi Tavares

Projeto e Análise de Algoritmos Projeto de Algoritmos Programação Dinâmica. Prof. Humberto Brandão

Problema da Mochila Booleana: Uma Solução Usando Programação Dinâmica. Gabriel Rosa Guilherme Alves

Análise e Complexidade de Algoritmos

Complexidade de Algoritmos. Edson Prestes

AED2 - Aula 11 Problema da separação e quicksort

Divisão e Conquista. Norton T. Roman. Apostila baseada nos trabalhos de Cid de Souza, Cândida da Silva e Delano M. Beder

Programação Dinâmica I SCC0210 Algoritmos Avançados (2/2011) Lucas Schmidt Cavalcante

Pedro Ribeiro 2014/2015

O Cálculo λ sem Tipos

Algoritmos de Ordenação

Problema de seleção de atividades. Aula 14. Exemplo. Algoritmos Gulosos. Algoritmos Gulosos. Intervalo: par ordenado de números

Análise de Complexidade para algoritmos iterativos e recursivos

Complexidade de Algoritmos

ESCOLA SECUNDÁRIA COM 3º CICLO D. DINIS 10º ANO DE MATEMÁTICA A Tema II Funções e Gráficos. Funções polinomiais. Função módulo.

Algoritmos e Estruturas de Dados I. Recursividade. Pedro O.S. Vaz de Melo

Análise e Projeto de Algoritmos

Paradigmas de Projeto de Algoritmos

Agenda. Complexidade Não Determinista A classe NP. A classe Co-NP Reduções de tempo polinomial. Definida por. Exemplos em:

Mergesort. Aula 04. Algoritmo Mergesort. Divisão e Conquista. Divisão e Conquista- MergeSort

> Princípios de Contagem e Enumeração Computacional 1/13

INE5403 FUNDAMENTOS DE MATEMÁTICA DISCRETA

É interessante comparar algoritmos para valores grandes de n. Para valores pequenos de n, mesmo um algoritmo ineficiente não custa muito para ser

SCC0601 Projeto de Algoritmos. Recursão

Mochila. Dados dois vetores x[1..n] e w[1..n], denotamos por x w o produto escalar

Análise e Projeto de Algoritmos

Programação dinâmica

Introdução à Programação / Programação I

CES-11. Noções de complexidade de algoritmos. Complexidade de algoritmos. Avaliação do tempo de execução. Razão de crescimento desse tempo.

Paradigmas de Projeto de Algoritmos

PCC104 - Projeto e Análise de Algoritmos

Sub Rotinas. Estrutura de Dados. Prof. Kleber Rezende

Técnicas Inteligência Artificial

Fundamentos de Programação

Estruturas Discretas

Projeto e Análise de Algoritmos Aula 4: Dividir para Conquistar ou Divisão e Conquista ( )

Informática para Ciências e Engenharias 2013/14. Teórica 7

Programação Dinâmica

Aula 22: Formulações com número exponencial de variáveis

Algoritmos Gulosos. Norton T. Roman

P, NP e NP-Completo. André Vignatti DINF- UFPR

Complexidade de Algoritmos. Edson Prestes

tipo e tamanho e com os "mesmos" elementos do vetor A, ou seja, B[i] = A[i].

1. Introdução Principais pontos de impacto da certificação Entrada na Aplicação Aplicação Não certificada...

Projeto e Análise de Algoritmos Aula 8: Algoritmos Gulosos (5)

Compiladores - Análise LL(1)

CIC 110 Análise e Projeto de Algoritmos I

Análise de algoritmos

Estruturas de Dados, Análise de Algoritmos e Complexidade Estrutural. Carlos Alberto Alonso Sanches

Carlos de Salles Soares Neto Segundas e Quartas, 17h40 às 19h10

Árvores. ! utilizada em muitas aplicações. ! modela uma hierarquia entre elementos. ! O conceito de árvores está diretamente ligado à recursão

CES-11. Algoritmos e Estruturas de Dados. Carlos Alberto Alonso Sanches

USANDO UM MÉTODO INDUTIVO PARA RESOLVER PROBLEMAS. Bruno Maffeo Departamento de Informática PUC-Rio

Documento Auxiliar do Conhecimento de Transporte Eletrônico

n Programação Dinâmica n Exemplo: Sequência de Fibonnaci n Problemas de Otimização n Multiplicação de Matrizes n Principios de Programação Dinâmica

Oficina de Python Prof. Me. José Carlos Perini

CAL ( ) MIEIC/FEUP Estruturas de Dados ( )

Complexidade de Algoritmos

Eduardo Camponogara. DAS-9003: Introdução a Algoritmos

Oficina de Introdução de Programação usando Linguagem Python Prof. Ms. Perini

Transcrição:

Programação Dinâmica Subsequência comum mais longa Em inglês, Longest Common Subsequence (LCS) Fernando Lobo Algoritmos e Estrutura de Dados II 1 / 23 Longest Common Subsequence (LCS) Dadas duas sequências, X = x 1 x 2... x m e Y = y 1 y 2... y n, encontrar uma subsequência comum a X e Y que seja o mais longa possível. Exemplo: X = n o c t u r n o Y = m o s q u i t e i r o LCS(X, Y ) = o t r o (também podia ser o u r o) 2 / 23

Algoritmo de força bruta Gerar todas as subsequências de X e verificar se também é subsequência de Y, e ir guardando a subsequência mais longa vista até ao momento. Complexidade? Θ(2 m ) para gerar todas as subsequências de X. Θ(n) para verificar se uma subsequência de X é subsequência de Y. Total: Θ(n 2 m ) É exponencial. Muito mau! 3 / 23 Será que podemos aplicar Programação Dinâmica? Se sim teremos de conseguir definir o problema recursivamente em termos de subproblemas. O n o de subproblemas tem de ser relativamente pequeno (polinomial em n e m) para que a Programação Dinâmica seja útil. Depois de definir o problema em termos de subproblemas, podemos resolver o problema de baixo para cima, começando pelos casos base e armazenando as soluções dos subproblemas. 4 / 23

Subestrutura óptima Vamos olhar para prefixos de X e Y. Seja X i o prefixo dos i primeiros elementos de X. Exemplo: X = n o c t u r n o X 4 = n o c t X0 = X3 = n o c X 8 = n o c t u r n o 5 / 23 Subestrutura óptima Seja X = x 1 x 2... x m e Y = y 1 y 2... y n. Seja Z = z 1 z 2... z k uma LCS entre X e Y. Três casos: 1. Se x m = y n, então z k = x m = y n e Z k 1 é uma LCS entre X m 1 e Y n 1. 2. Se x m y n e z k x m, então Z é uma LCS entre X m 1 e Y n. 3. Se x m y n e z k y n, então Z é uma LCS entre X m e Y n 1. 6 / 23

Demonstração do caso 1 Caso 1: Se x m = y n, então z k = x m = y n e Z k 1 é uma LCS entre X m 1 e Y n 1. Teremos de provar que z k = x m = y n. Suponhamos que tal não é verdade. Então a subsequência Z = z 1 z 2... z k x m é uma subsequência comum a X e Y e tem comprimento k + 1. Contradiz o facto de Z ser uma LCS entre X e Y. 7 / 23 Demonstração do caso 1 (cont.) Agora temos de provar que Z k 1 é uma LCS entre X m 1 e Y n 1. Suponhamos que existe uma subsequência W comum a X m 1 e Y n 1 que é mais longa que Z k 1. comprimento de W k. A subsequência W = W x m é comum a X e Y e tem comprimento k + 1. Contradiz o facto de Z ser uma LCS entre X e Y. 8 / 23

Demonstração dos casos 2 e 3 Caso 2: Se x m y n e z k x m, então Z é uma LCS entre X m 1 e Y n. Suponhamos que existe uma subsequência W comum a X m 1 e Y n com comprimento > k. Então W é uma subsequência comum entre X e Y. = Contradiz o facto de Z ser uma LCS entre X e Y. Caso 3: Se x m y n e z k y n, então Z é uma LCS entre X m e Y n 1. A demonstração do caso 3 é análoga à do caso 2. 9 / 23 Resumindo Podemos definir LCS(X m, Y n ) em termos de subproblemas., se m = 0 ou n = 0 LCS(X m, Y n ) = LCS(X m 1, Y n 1 ) x m, se x m = y n LCS(X m 1, Y n ) ou LCS(X m, Y n 1 ), se x m y n 10 / 23

Comprimento de LCS(X, Y ) Vamos tentar primeiro resolver um problema mais simples: Obter LCS(X, Y ) o comprimento de LCS(X, Y ) Seja c[i, j] = LCS(X i, Y j ) Queremos obter c[m, n] 11 / 23 Definição recursiva de c[i, j] c[i, j] = 0, se i = 0 ou j = 0 c[i 1, j 1] + 1, se i, j > 0 e x i = y j max(c[i 1, j], c[i, j 1]), se i, j > 0 e x i y j 12 / 23

Algortimo recursivo LCS-Length-Rec(X, Y, i, j) if i == 0 or j == 0 return 0 elseif X [i] == Y [j] return LCS-Length-Rec(X, Y, i 1, j 1) + 1 else a = LCS-Length-Rec(X, Y, i 1, j) b = LCS-Length-Rec(X, Y, i, j 1) return max(a, b) Chamada inicial: LCS-Length-Rec(X, Y, m, n) Tal como em Fib-Rec e Comb-Rec, a árvore dá origem a muitos subproblemas repetidos. O algoritmo é exponencial. Mas o número de subproblemas distintos = m n. 13 / 23 Podemos usar Programação Dinâmica LCS-Length-DP(X, Y ) m = X.length n = Y.length for i = 1 to m c[i, 0] = 0 for j = 0 to n c[0, j] = 0 for i = 1 to m for j = 1 to n if X [i] == Y [j] c[i, j] = c[i 1, j 1] + 1 elseif c[i 1, j] c[i, j 1] c[i, j] = c[i 1, j] else c[i, j] = c[i, j 1] return c[m, n] 14 / 23

Demo c[i, j] é preenchida linha a linha, da esquerda para a direita. 15 / 23 Como obter a LCS própriamente dita? O nosso algoritmo apenas obteve o comprimento da LCS. A ideia é alterar o código de LCS-Length-DP e, de cada vez que obtemos um c[i, j], registamos como é que ele foi obtido. Isso permite-nos reconstruir a solução. 16 / 23

Aqui vai o código alterado LCS-Length-DP-v2(X, Y ). for i = 1 to m for j = 1 to n if X [i] == Y [j] c[i, j] = c[i 1, j 1] + 1 b[i, j] = elseif c[i 1, j] c[i, j 1] c[i, j] = c[i 1, j] b[i, j] = else c[i, j] = c[i, j 1] b[i, j] = return c[m, n], b 17 / 23 Demo As setas, e são armazenadas em b[i, j]. b[i, j] indica o subproblema escolhido para obter c[i, j]. 18 / 23

Uma vez tendo a informação em b, podemos obter uma LCS entre X e Y. A chamada inicial é Print-LCS(b, X, m, n) Print-LCS(b, X, i, j) if i == 0 or j == 0 return // Não faz nada if b[i, j] == Print-LCS(b, X, i 1, j 1) print X [i] elseif b[i, j] == Print-LCS(b, X, i 1, j) else Print-LCS(b, X, i, j 1) 19 / 23 Complexidade A complexidade é Θ(m n) A Programação Dinâmica reduziu a complexidade de exponencial para polinomial. No livro têm mais exemplos de problemas resolvidos com Programação Dinâmica. 20 / 23

Versão memoized de LCS-Length LCS-Length-Memoized(X, Y ) m = X.length n = Y.length for i = 0 to m for j = 0 to n c[i, j] = unknown return M-LCS-Length(X, Y, m, n) 21 / 23 M-LCS-Length(X, Y, i, j) if c[i, j] == unknown if i == 0 or j == 0 c[i, j] = 0 elseif X [i] == Y [j] c[i, j] = M-LCS-Length(X, Y, i 1, j 1) + 1 else a = M-LCS-Length(X, Y, i 1, j) b = M-LCS-Length(X, Y, i, j 1) c[i, j] = max(a, b) return c[i, j] 22 / 23

Como aplicar a Programação Dinâmica? Para aplicarmos Programação Dinâmica ou Memoization para resolver um problema, temos de fazer 4 coisas: 1. Caracterizar a estrutura de uma solução óptima. 2. Definir o valor da solução óptima recursivamente em termos de subsoluções óptimas. 3. Calcular o valor de uma solução óptima de baixo para cima (no caso de P.D.) ou de cima para baixo (no caso de Memoization). 4. Obter a solução óptima através da informação calculada e armazenada no passo 3. 23 / 23