Você está na página 1de 28

MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Prof. Raquel C. de Melo-Minardi, Ph.D.


Departamento de Ciência da Computação / UFMG
11 de agosto de 2020

1 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Provavelmente o software mais famoso e usado na bioinformática de todos os tempos é o BLAST. A maioria
dos biólogos certamente usa o BLAST com bastante confiança devido ao fato de ele ser um algoritmo tão
difundido. Entretanto, grande parte dos usuários não conhece o algoritmo por trás do software, ou seja, não
tem a menor idéia de como ele funciona. Em casos mais graves, chega-se a usar o BLAST em situações nas
quais o seu uso nem mesmo seria o mais correto ou recomendado.
Independentemente de qual software de bioinformática você pretende utilizar, é imprescindível conhecer o
algoritmo por trás do mesmo e ter certeza de que ele é adequado para o problema que se tem em mãos.
Além disso, a grande maioria dos algoritmos utiliza argumentos ou parâmetros de entrada que balizam o seu
funcionamento mudando completamente o tipo de resultados que se pode obter. É muito comum entre os
estudantes de graduação e mesmo pós-graduação o uso de softwares de terceiros como uma “caixa preta”
da qual o funcionamento é completamente desconhecido. É importante destacar que qualquer software que
receba uma entrada formatada de acordo com suas regras irá gerar como saída um resultado. Entretanto,
esse resultado por não ser válido, pode não ser o melhor possível ou o mais adequado dependendo da forma
e dos parâmetros como o algoritmo foi utilizado. Muitas vezes se usa o padrão ou default do software. Essa
prática também não é segura visto que esses padrões podem até ter sido calibrados para alguma base de
dados genérica mas podem não ser, e provavelmente não serão, os melhores para a sua base de dados
particular. Dessa fprma, é preciso um trabalho experimental bastante detalhado para avaliação e calibração
desses parâmetros da forma mais adequada ao seu problema.

Sumário

Com essa breve introdução, queremos lhe motivar a sempre:


• Entender em detalhes o funcionamento do algoritmo que está por trás do software que você utiliza
• Conhecer e compreender os parâmetros que guiam o funcionamento desse algoritmo e o seu
impacto nos resultados
• Desenhar e executar uma metodologia de avaliação dos parâmetros, resultados e significado biológico
do mesmo visando sempre utilizar os parâmetros mais adequados ao seu problema.
Mais ainda, em um cenário mais amplo, conhecer os diversos algoritmos que resolvem um mesmo
problema de forma a:
• Escolher o melhor e mais eficiente algoritmo para resolução do problema
• Utilizando os parâmetros mais acertados para o mesmo

Em toda a bioinformática, há diversos algoritmos conhecidos e amplamente utilizados. Nesse curso, não
temos o objetivo de abrangermos toda a área obviamente. Como focamos no desenvolvimento de algoritmos,
no aprendizado da linguagem Perl e na análise teórica da complexidade de algoritmos, nesta unidade,
gostaríamos de abordar alguns exemplos de algoritmos muito conhecidos em bioinformática para ilustrar os
conceitos vistos até então. Um dos principais que podemos mencionar são os algoritmos de alinhamento de
sequências.

2 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Como dissemos anteriormente, muitos estudantes e pesquisadores usam o software BLAST sem ter ao
menos uma vaga idéia de como ele funciona podendo aplicá-lo em situações inadequadas ou mesmo
interpretar incorretamente seus resultados. Muitos livros de bioinformática tem trazido uma abordagem nessa
linha que trata do uso de software como se fossem um protocolo computacional tratando de configuração de
parâmetros, características de aplicações específicas do software e alguns outros detalhes sem, no entanto,
entrar em detalhes sobre o funcionamento do algoritmo. Usa-se o software com base em uma receita
computacional sem ter um entendimento sobre o algoritmo.
Acreditamos que um estudante de bioinformática precisa, mais que de protocolos simplificados de como usar
um software (no estilo de um manual), buscar se desenvolver intelectualmente na ciência computacional e ser
capaz não só entender os algoritmos como de projetá-los e implementá-los. Há inúmeras idéias algorítmicas
que podem ser usadas para resolver diversos problemas em bioinformática. Acreditamos que entender essas
idéias tem um grande valor intelectual e, para o estudante ingressando na área, é um investimento de longo
prazo. Pense que os protocolos mudam rapidamente, métodos e softwares se tornam obsoletos, mas os
paradigmas computacionais não se mostram tão voláteis mas são constantemente reusados nos mais
diversos contextos.

Algoritmos para alinhamento de sequências


JOGO DAS FICHAS
Alice e Bob tem duas pilhas de fichas e estão a jogar o seguinte jogo: a cada rodada um jogador pode retirar
uma pedra de uma pilha ou retirar uma pedra de cada uma das duas pilhas; a seguir, é a vez do outro
jogador; ganha a partida quem tirar a última pedra. Alice é a primeira a jogar.

Desafio

Pense sobre o problema e nos diga se:


1. Há uma estratégia para vencer o jogo?
2. Algum dos jogadores (o primeiro ou o segundo) tem alguma vantagem?

Bob tentar sistematizar o conhecimento sobre o jogo e reflete sobre as possíveis variações de jogos que
podem ser jogados com pilhas de até 10 fichas (10+10). Ele nota que é bastante complexo e decide,
tomando uma abordagem mais reducionista, refletir sobre o jogo com 2 fichas em cada pilha (2+2). Ele nota
rapidamente que o segundo jogador, no caso ele, sempre ganha e decide então, escrever a receita para
vencer sempre o jogo:

• Se Alice pegar uma ficha de cada pilha, ele pegará as 2 restantes e vencerá o jogo
• Se Alice pegar apenas uma ficha de uma das pilhas, ele pegará apenas 1 ficha da mesma pilha. Assim,
Alice só terá uma pilha para pegar apenas uma pedra, deixando a última para Bob que vencerá o jogo
novamente

3 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Bob então presume que ele vencerá todo jogo n+n. Ele continua jogando e aplicando seu algoritmo para
vitória e nota que ele perde. Ele começa então a desenvolver uma estratégia para o jogo 3+3 e nota que há
diversas estratégias possíveis ficando sem esperança de conseguir um algoritmo para o jogo 10+10 pois Alice
pode tomar uma enorme variedade de possíveis jogadas e estratégias.
Enquanto isso Alice, que já cursou a disciplina de Algoritmos, nota que sempre perde o jogo 2+2 e não
desiste de encontrar uma receita para vencer jogos 3+3. Ela nota que a forma como Bob definiu seu
algoritmo de forma textual não ajudou muito pois há muitas possibilidades. Ela tenta organizar suas idéias
usando um outro tipo de modelo: o matricial. Cada posição da (i, j) da matriz descreve uma possível jogada
que ela fará em um jogo i+j (i pedras da pilha A e j na pilha B). Ela começa a preencher a matriz com os
símbolos:
• *: não vale a pena jogar pois sempre perderá se o oponente conhecer a regra da vitória
• ←: deve retirar uma ficha da pilha B
• ↑: deve retirar uma ficha da pilha A
• ↖: deve retirar uma ficha de cada pilha
Veja a matriz que Alice gerou com esse algoritmo para vencer o jogo:

0 1 2 3 4 5 6 7 8 9 10

0 * ← * ← * ← * ← * ← *

1 ↑ ↖ ↑ ↖ ↑ ↖ ↑ ↖ ↑ ↖ ↑

2 * ← * ← * ← * ← * ← *

3 ↑ ↖ ↑ ↖ ↑ ↖ ↑ ↖ ↑ ↖ ↑

4 * ← * ← * ← * ← * ← *

5 ↑ ↖ ↑ ↖ ↑ ↖ ↑ ↖ ↑ ↖ ↑

6 * ← * ← * ← * ← * ← *

7 ↑ ↖ ↑ ↖ ↑ ↖ ↑ ↖ ↑ ↖ ↑

8 * ← * ← * ← * ← * ← *

9 ↑ ↖ ↑ ↖ ↑ ↖ ↑ ↖ ↑ ↖ ↑

10 * ← * ← * ← * ← * ← *

Por exemplo, se ela enfrentará um jogo 3+3, ela encontra o símbolo “↖" dentro da célula (3, 3) indicando que
ela deve pegar uma ficha de cada topo de pilha. Isso faz com que Bob enfrente a seguir um jogo 2+2,
marcado com “*" indicando que, não importa a jogada que ele faça, Alice vencerá. Suponha que, ao invés
disso, Bob retire uma pedra da pilha B, deixando um jogo 2+1 para Alice que consultará a célula (2, 1) da
tabela que lhe indicará que deve tirar uma outra ficha da pilha B, deixando apenas duas fichas na pilha A

4 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

(Alice vencerá). Se Bob, ao invés disso, retirar uma ficha da pilha A, Alice deverá consultar a célula (1, 2) que
lhe indicará que deve retirar uma ficha da pilha A deixando duas fichas na pilha B para que vença novamente.
Bob ficou impressionado com a tabela construída por Alice mas percebe que não consegue construir uma
matriz semelhante para um jogo 20+20. O problema é que Bob não cursou a disciplina de Algoritmos.

Você deve estar se perguntando: Mas o que esse jogo tem a ver comigo? E o que tem a ver com alinhamento
de sequências?

Esse jogo e o respectivo algoritmo nada mais são do que o clássico algoritmos para alinhamento de
sequências. Apesar de, a primeira vista, não parecer que as fichas não tem nada a ver com a sequência de
DNA ou de aminoácidos, a idéia computacional usada para resolver ambos os problemas é a mesma. O fato
de Bob estar não ser capaz de encontrar a estratégia para o jogo, indica que ele não entende como os
algoritmos de alinhamento funcionam. Mesmo que ele use o BLAST diariamente, ele não entende a idéia por
trás do método e, como ele não conseguiu encontrar uma estratégia para o jogo de 10+10, ele
provavelmente falhará quando confrontado por um novo problema de alinhamento. Bob precisa se tornar um
bioinformata de verdade e aprender a pensar algoritmicamente sobre os problemas biológicos.

Esse paradigma de programação muito utilizado em Ciência da Computação é conhecido como


programação dinâmica.

Paradigma é um exemplo que serve como modelo; um padrão.


—Dicionário do Google

Em computação, paradigmas são formas ou técnicas de projeto de algoritmos comumente utilizadas, ou seja,
para as quais há diversos exemplos de diferentes algoritmos. Alguns exemplos não tratados nesse curso
incluem: indução, recursividade, algoritmos de tentativa e erro, divisão e conquista, balanceamento,
algoritmos gulosos e algoritmos aproximados [Ziviani, 2004].

Programação dinâmica é um paradigma de projeto de algoritmos baseada no cálculo da solução de


todos os possíveis subproblemas de um problema, partindo dos menores para os maiores, armazenando
os resultados em uma matriz ou tabela. Uma grande vantagem desse paradigma é que uma vez que um
subproblema é resolvido e sua resposta é armazenada na matriz, a solução desse subproblema nunca mais
será recalculada.
—Nívio Ziviani

Você percebe agora que o problema do jogo das fichas é resolvido usando programação dinâmica? Note que
quando Bob quer resolver um problema 20+20, ele necessariamente passará pela solução de um problema
10+10 e por um 2+2 respectivamente. Uma vez o menor subproblema calculado, ele nunca será revisitado
mas sim comporá as soluções dos problemas maiores. Dizemos que um problema para poder ser resolvido
através de programação dinâmica precisa ter subestrutura ótima, o que certamente é o caso no nosso jogo.

5 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Interessantemente, um alinhamento par-a-par local de sequências (problema resolvido pelo BLAST), é um


problema que tem subestrutura ótima. Reflita sobre essa questão.

PROBLEMA DO TURISTA EM MANHATTAN


Antes de entrar no alinhamento de sequências, mostraremos um outro problema que também é resolvido da
mesma forma. Esse problema se chama O problema do Turista em Manhattan e servirá para desenvolver sua
intuição para que compreenda mais facilmente o problema do alinhamento de sequências.

Esse é mapa do centro de Manhattan onde se pode ver inúmeros pontos turísticos como o Carnegie Hall, o
Rockfeller Center, a Times Square, entre outros. Uma característica interessante dessa região que se pode
notar através do mapa é a organização bem retangular dos quarteirões. Imagine que você está em Manhattan
e que está em uma excursão que lhe deixará na esquina entre a 59th Street e a 8th Avenue e lhe buscará no
Chrysler Building na esquina entre a 42nd Street e a Lexington Avenue. Há inúmeras atracões turísticas na
região entre esses pontos e você deseja organizar seu trajeto de forma a visitar o máximo de pontos turísticos
quanto possível sem se preocupar com o tempo.
Veja um mapa mais simplificado retirado de [Jones e Pevzner, 2004] de onde todo esse exemplo foi retirado:

6 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Scanned by CamScanner

7 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Como todos os quarteirões são retangulares, essa região pode ser facilmente representada como um grid
como o da figura a seguir no qual temos nós (círculos) representando as esquinas e arestas (setas)
representando os trechos das ruas entre as esquinas. Os rótulos das arestas indicam quantos pontos
turísticas há naquele trecho da rua.

Chamaremos o primeiro nó do canto superior esquerdo de fonte e o último do canto inferior direito de
sumidouro. O objetivo do turista é sair da fonte e chegar no sumidouro com o maior número possível de
pontos turísticos visitados. Note que ele só pode caminhar de cima para baixo e da esquerda para a direita
não sendo permitidas voltas.
Mais formalmente, o problema pode ser definido assim:

Problema do turista en Manhattan

Encontre o caminho máximo em um grid rotulado

Entradas: Um grid rotulado G com pesos e dois vértices distintos: fonte e sumidouro
Saída: O caminho máximo em G entre fonte e sumidouro

Como você resolveria esse problema? Usar um método de força bruta seria uma possibilidade e consistiria
em buscar todos os possíveis caminhos entre fonte e sumidouro. Conforme vimos no capítulo anterior, essa

8 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

solução seria proibitiva em termos de custo computacional que seria fatorial. Poderíamos então usar uma
abordagem “gulosa" que consiste em, a cada esquina, olhar as ruas adiante escolhendo a opção
aparentemente mais vantajosa localmente dentre as duas possíveis ruas a tomar. Essa solução pode parecer
interessante em determinado momento mas pode levar a uma solução péssima levando o turista para um
local com poucas ou nenhuma atração e levando a uma solução ruim.
Felizmente, esse problema tem subestrutura ótima e pode ser resolvido por programação dinâmica. Ao invés
de solucionar o problema de ir da fonte (0, 0) ao sumidouro (n, m), resolveremos o problema mais geral e
encontrar o caminho máximo entre a fonte e um vértice arbitrário (i, j), com 0 ≤ i ≤ n, 0 ≤ j ≤ m. Denotaremos
o menor caminho por si, j se do sn.m a solução do Problema do Turista em Manhattan. Aparentemente,
estamos escolhendo um problema geral ainda mais difícil mas isso não é verdade pois, para resolver o
Problema do Turista em Manhattan, precisaremos resolver os subproblemas por programação dinâmica,
então, é a mesma dificuldade.
Comecemos por s0, j (para 0 ≤ j ≤ m) que é um problema simples bastando que o turista se mova sempre à
direita e acumulando as pontuações. A seguir, podemos fazer o mesmo para si, 0 (para 0 ≤ i ≤ n) em que o
turista se moverá sempre para baixo e acumulando as pontuações. Note que colocamos a soma da
pontuação do caminho da fonte até o ponto si, j como um rótulo dentro do nó.

A seguir, podemos preencher tanto a linha 2 quando a coluna 2 e assim sucessivamente. Note que as novas
soluções que vão sendo geradas para cada problema (cada nó é um tamanho de problema) são compostas
pelas soluções dos subproblemas menores que já foram calculados. Por exemplo, a célula s1, 1 usará as
soluções s0, 1 ou s1, 0. Nesse caso, a melhor solução será vir de s1, 0 somando o valor da aresta que é 3:

9 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Estendendo essa solução para a coluna teremos:

10 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

E, finalmente, termos a matriz completa com os caminhos máximos saindo da fonte s0, 0 para todos os
possíveis si, j com 0 ≤ i ≤ n, 0 ≤ j ≤ m teremos:

Interessantemente, essa matriz pode ser preenchida em três possíveis sequências:

Não importa a ordem em que seja preenchida, seu conteúdo será o mesmo.

11 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

DISTÂNCIAS ENTRE SEQUÊNCIAS


Voltando ao nosso problema original que era o alinhamento de sequências, vocês podem imaginar que esse
seja um problema genuinamente de bioinformática quando, na verdade, já era um problema bem resolvido e
mais antigo em ciência da computação. Uma das formas que já utilizávamos de calcular a distância ou
dissimilaridade de sequências era a chamada distância de Hamming. Ela foi criada em 1950 para detecção
e correção de erros.

Na  teoria da informação, a  distância de Hamming  entre duas  cadeias de caracteres  de mesmo
comprimento é o número de posições nas quais elas diferem entre si. Vista de outra forma, ela corresponde
ao menor número de substituições necessárias para transformar uma sequência na outra, ou o número de
erros que transformaram uma na outra.
Exemplo:
As sequências
ACTGACTG
TCAGCCTG
tem a distância de Hamming 3.

—Wikipedia

Contudo, as sequências ATATATAT e TATATATA são muito diferentes em termos da distância de Hamming (8)
mas poderiam ser consideradas extremamente similares se inseríssemos uma lacuna, arredando uma das
sequências uma posição para a frente. É o que os biólogos chamam de gap em um alinhamento de
sequências.

Em 1966, Vladimir Levenshtein introduziu a noção da distância de edição entre duas sequências:

A distância de edição ou distância de Levenshtein entre duas sequências é o número mínimo de


edições dos seguintes tipos são necessárias para transformar uma sequência na outra:
• Inserção de um caracter em uma sequência
• Deleção de um caracter em uma sequência
• Substituição de um caracter por outro em uma sequência
Exemplo:
As sequências
ATAGATAT-
-TACATATA
tem a distância de Levenshtein 3:
• A deleção de um “A” na 1a posição
• A substituição de um “G" por um “C" na 4a posição
• A inserção de um “A" na 9a posição

—Jones e Pevzner

12 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Desafio

Implemente em Perl duas funções que recebam como argumento duas sequências e retornem:
1. A distância de Hamming
2. A distância de Levenshtein

Ao contrário da distância de Hamming, a distância de Levenshtein permite comparar sequências de


tamanhos diferentes. Estranhamente, Levenshtein propôs essa definição de distância sem nunca ter
apresentado um algoritmo para calculá-la.

ALGORITMO DE NEEDLEMAN-WUNSCH
Esse algoritmo foi descoberto e redescoberto inúmeras vezes em áreas que vão desde reconhecimento de
fala até, obviamente, biologia molecular. Apesar de haverem detalhes que diferem nas diversas aplicações,
todas são essencialmente programação dinâmica.

O alinhamento de suas sequências v (com n caracteres) e w (com m caracteres) pode ser visualizado assim:

v A T C G T - A - C

w A T - G T T A T -

Note que esse alinhamento poderá ter, no máximo (n+m) posições. As colunas que contêm o mesmo caracter
são chamadas de matches (casamentos). As colunas que contêm espaços são chamadas indels (inserções
e deleções) sendo que quando a linha superior contem um caracter é uma deleção e quando ela contêm um
espaço, é uma inserção. O alinhamento exemplificado acima tem 5 matches, 0 mismatches (pareamento
de caracteres não idênticos) e 4 indels. Esse alinhamento poderia ser representado como o seguinte grid ou
matriz na qual a primeira sequência está representada nas colunas e a segunda nas linhas:

13 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Essa matriz é central em todo algoritmo de alinhamento e o caminho destacado representa o melhor
alinhamento entre s0,0 e sn,m. É interessante notar que as arestas no caminho máximo representam
impressões:

• Horizontais: significam deleções na primeira sequência v, ou seja, um caracter na primeira sequência é


impresso e um gap é impresso na segunda sequência w.

• Verticais: significam inserções na primeira sequência v, ou seja, um gap é impresso na primeira


sequência e um caracter é impresso na segunda sequência w.

• Diagonais: significam um match ou mismatch, ou seja, um caracter é impresso em cada uma das
sequências v e w podendo ser iguais ou diferentes.

Falta então um esquema de pontuação que seja capaz de julgar o mérito dos diversos possíveis
alinhamentos. Esse esquema de pontuação fará algo similar ao que o número de pontos turísticos em cada
quarteirão fazia no Problema do Turista de Manhattan. Teremos, então, o custo de admitir matches e
mismatches bem como dos indels.

Suponha, por simplicidade, que usaremos um esquema demasiadamente simplificado no qual pontuamos
“+1" para um match e “0”, caso contrário. Nessa primeira versão do algoritmo, não contemplaremos um
mismatch visto que o esquema de pontuação permite caminhamento em diagonal apenas no caso de
igualdade entre os caracteres. Destacamos que não há perda de generalidade pois bastaria criar uma
pontuação diferenciada para mismatches e o mesmo algoritmo poderia ser aplicado.

14 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Esse problema é clássico em ciência da computação e é chamado de Problema da Máxima


Subsequência Comum (do inglês Longest Common Subsequence - LCS) e iremos abordá-lo como um
aquecimento.

Problema da Máxima Subsequência Comum

Encontre a máxima subsequência comum entre duas sequências

Entradas: Duas sequências, v e w


Saída: A máxima subsequência comum entre v e w

Para resolver o LCS, criaremos uma matriz de programação dinâmica colocando a primeira sequência como
as colunas e a segunda como as linhas conforme a seguir e seguindo o seguinte critério si, j = max (si-1, j, si, j-1
e si-1, j-1+1 (desde que v[i] = w[j]):

v A T C G T A C

w 0 0 0 0 0 0 0 0

A 0

T 0

G 0

T 0

T 0

A 0

T 0

Note que preenchemos a primeira linha (s0,j) e coluna (si,0) com 0s para inicializar a pontuação do alinhamento.
De forma análoga ao que fizemos no Problema do Turista de Manhattan, vamos preenchendo essas matrizes
com as pontuações obtidas nos possíveis alinhamentos. Comecemos com a célula s1, 1 que corresponde ao
problema de se alinhar as sequências “A” com “A”. Nesse caso, teríamos um match e pontuaríamos “+1” e
deixando uma marca diagonal.

15 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

v A T C G T A C

w 0 0 0 0 0 0 0 0

A 0 1↖

T 0

G 0

T 0

T 0

A 0

T 0

Podemos prosseguir, calculando os problemas de toda a linha s1, j. Note que a célula s1, 2, representaria um
mismatch de “A” com “T" e pontuaria “0”. Essa pontuação tem de ser somada com a de s1, 1 pois a posição
corrente indica o alinhamento de “A" com “AT” e valeria “1" com a marcação de horizontal pois veio de s1, 1.

v A T C G T A C

w 0 0 0 0 0 0 0 0

A 0 1↖ 1←

T 0

G 0

T 0

T 0

A 0

T 0

Preenchendo a linha, teríamos:

16 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

v A T C G T A C

w 0 0 0 0 0 0 0 0

A 0 1↖ 1← 1← 1← 1← 1← 1←

T 0

G 0

T 0

T 0

A 0

T 0

Por fim, podemos continuar o preenchimento da matriz linha a linha:

v A T C G T A C

w 0 0 0 0 0 0 0 0

A 0 1↖ 1← 1← 1← 1← 1← 1←

T 0 1↑ 2↖ 2← 2← 2← 2← 2←

G 0 1↑ 2↑ 2← 3↖ 3← 3← 3←

T 0 1↑ 2↑ 2← 3↑ 4↖ 4← 4←

T 0 1↑ 2↑ 2← 3↑ 4↑ 4← 4←

A 0 1↑ 2↑ 2← 3↑ 4↑ 5↖ 5←

T 0 1↑ 2↑ 2← 3↑ 4↑ 5↑ 5←

Dessa matriz, podemos perceber que o alinhamento de pontuação máxima terá valor 5. Além disso, seguindo
os ponteiros partindo da célula sn, m até chegar a célula s0, 0, podemos gerar o alinhamento:

v A T C G T - A - C

w A T - G T T A T -

Note que esse alinhamento é construído de trás para frente, ou seja, da direita para a esquerda. Partindo da
célula s7, 7, na qual temos um “5←”, imprimiremos no alinhamento o caracter “C" da sequência v e um gap na
sequência w. Recapitulando, essa decisão se deve à direção apontada pelo ponteiro:

17 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

• Horizontais (←): significam deleções na primeira sequência v, ou seja, um caracter na primeira sequência
é impresso e um gap é impresso na segunda sequência w.

• Verticais (↑): significam inserções na primeira sequência v, ou seja, um gap é impresso na primeira
sequência e um caracter é impresso na segunda sequência w.

• Diagonais (↖): significam um match, ou seja, um caracter é impresso em cada uma das sequências v e
w.

Desafio

Implemente em Perl o algoritmo da Máxima Subsequência Comum.


Dica: use uma matriz para armazenar as pontuações e outra para armazenar os ponteiros.

A resposta a esse desafio será disponibilizada na íntegra nos códigos fontes do curso. Vamos explicar passo
a passo o algoritmo que desenvolvemos. Nosso programa principal ficou como o seguinte:

# PROGRAMA PRINCIPAL
v = ['*', 'A', 'T', 'C', 'G', 'T', 'A', 'C']
w = ['*', 'A', 'T', 'G', 'T', 'T', 'A', 'T']
lcs(v, w)

Esse programa declara e inicializa dois arranjos contendo as sequências v e w a serem alinhadas e chama a
função “lcs" (Longest Common Subsequence) passando esses dois arranjos por referência. A seguir
implementamos a função:

18 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

1 def lcs(v, w):


2 pontuacao = []
3 ponteiros = []
4 pontuacao = [0]*len(v)
5 ponteiros = ['']*len(v)
6 for i in range(0, len(w)):
7 pontuacao[i] = [0]*len(v)
8 ponteiros[i] = ['']*len(v)
9 for i in range(0, len(w)):
10 ponteiros[i][0] = '|'
11 for j in range(0, len(v)):
12 ponteiros[0][j] = ‘_'
13 for i in range(1, len(w)):
14 for j in range(1, len(v)):
15 pontuacao[i][j] = maximo(v[j], w[i], pontuacao[i-1][j], pontuacao[i]
[j-1], pontuacao[i-1][j-1])
16 ponteiros[i][j] = ponteiro(v[j], w[i], pontuacao[i-1][j],
pontuacao[i][j-1], pontuacao[i-1][j-1])
17 imprimeMatriz(v, w, pontuacao, ponteiros)
18 geraAlinhamento(v, w, pontuacao, ponteiros)

Na linha (2), recebemos como argumento as referências para dois arranjos com as sequências v e w. A seguir,
(3) declaramos duas matrizes que receberão as pontuações dos alinhamentos “pontuacao” e os ponteiros
para recuperar o caminho do alinhamento máximo “ponteiros“. Note que a matriz ilustrada anteriormente é
aqui armazenada em dois arranjos diferentes indexados pelos mesmos índices. Em (4), adicionamos um “0”
ao início de cada sequência v e w. Fazemos isso para que os índices que usaremos para caminhar nas
sequências correspondam aos índices para o caminhamento nas matrizes “pontuacao“ e “ponteiros“. Isso é
necessário pois essas matrizes tem a primeira linha e a primeiro coluna preenchida com “0”s. Os laços em (4)
e (5) servem para inicializar com “0”s a matriz pontuação e com vazio, respectivamente, a matriz de ponteiros.

Os laços em (9) e (11) são a implementação do algoritmo para preenchimento da matriz. Em (15),
implementamos o critério si, j = max (si-1, j, si, j-1 e si-1, j-1+1) (desde que v[i] = w[j]). A função “maximo” será
implementada a seguir. Na linha (16), o mesmo critério é aplicado através da implementação que será
explicada a seguir da função “ponteiro” que serve para decidir segundo o critério de pontuação para qual das
três células vizinhas (si-1, j, si, j-1 e si-1, j-1) a célula atual deverá apontar (↑, ← e ↖).

Por fim, as linhas (17) e (18) são chamadas a outras funções que detalharemos a seguir que imprimem as
matrizes de pontuação e ponteiros e realizam a impressão do alinhamento de pontuação máxima calculado.

Veja a seguir a implementação das funções auxiliares.

19 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

1 def maximo(c1, c2, cima, lado, diagonal):


2 if (c1 == c2 and (diagonal+1) >= cima and (diagonal+1) >= lado):
3 diagonal = diagonal+1
4 return diagonal
5 elif (lado >= cima and lado >= diagonal):
6 return lado
7 else:
8 return cima

11 def ponteiro(c1, c2, cima, lado, diagonal):


12 if (c1 == c2 and (diagonal+1) >= cima and (diagonal+1) >= lado):
13 return '\\'
14 elif (lado >= cima and lado >= diagonal):
15 return '_'
16 else:
17 return ‘|'

21 def imprimeMatriz(v, w, pontuacao, ponteiros):


22 print('\t', end='');
23 for j in range(0, len(v)):
24 print(v[j], end='\t')
25 print()
26 for i in range(0, len(w)):
27 print(w[i], end='\t')
28 for j in range(0, len(v)):
29 print(pontuacao[i][j], ponteiros[i][j], end='\t',
sep='')
30 print()
31 print()

Nas linhas (1) a (8) definimos a função “maximo" que conforme explicado implementa simplesmente o
seguinte critério si, j = max (si-1, j, si, j-1 e si-1, j-1+1) (desde que v[i] = w[j]). Para tal, ela recebe como argumentos
(1) os caracteres “c1" e “c2" que estão sendo avaliados e os valores das pontuações nas células vizinhas
(“lado”, “cima”, “diagonal”). Note que, caso os caracteres sejam iguais e a pontuação da célula em diagonal
seja a maior, retornamos o valor somado de “+1" (3). Caso contrário, podemos decidir vir da célula do lado (6)
ou de cima (8).

20 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

A função “ponteiro” implementada nas linhas (11) a (17) é praticamente idêntica à função “maximo" com o
diferencial de retornar símbolos “|”, “_” e “\” para representar respectivamente ↑, ← e ↖.

A função “imprime Matriz” implementada nas linhas (21) a (31) recebe como argumentos (21) as referências
para as sequências v e w e as matrizes de pontuação e ponteiros e as percorre imprimindo a matriz de
programação dinâmica gerada para simples conferência e entendimento.

1 def geraAlinhamento(v, w, pontuacao, ponteiros):


2 ali_v = ''
3 ali_w = ''
4 i = len(w)-1
5 j = len(v)-1
6 while ((i!=0) or (j!=0)):
7 if (ponteiros[i][j] == '\\'):
8 ali_v = v[j] + ali_v
9 ali_w = w[i] + ali_w
10 i-=1
11 j-=1
12 elif (ponteiros[i][j] == '_'):
13 ali_v = v[j] + ali_v
14 ali_w = '_' + ali_w
15 j-=1
16 else:
17 ali_v = '_' + ali_v
18 ali_w = w[i] + ali_w
19 i-=1
20 print(pontuacao[len(w)-1][len(v)-1])
21 print(ali_v)
22 print(ali_w)

A função “geraAlinhamento” é uma função muito importante pois é ela quem reconstrói o alinhamento a partir
das matrizes de programação dinâmica calculadas pela função “lcs”. Ela recebe como argumentos (1) as
referências para as sequências v e w e as matrizes de pontuação e de ponteiros e as percorre da célula sn,m
em direção à célula s0,0 emitindo nas variáveis arranjo “ali_v“ e “ali_w” (2-3) caracteres ou gaps conforme
direção do ponteiro da célula adequada da matriz de ponteiros (7), (12) ou (16). Como explicado
anteriormente, caso a célula seja um ponteiro ← (“_”), emitimos um caracter em v e um gap em w indicando
uma deleção; caso a célula seja um ponteiro ↑, (“|”), emitimos um caracter em w e um gap em v indicando
uma inserção e caso a célula seja um ponteiro ↖, (“\”), emitimos um caracter em v e outro em w indicando um
match. Em cada caso as variáveis “i” e / ou “j” são decrementadas para seguir o caminhamento indicado pelo
ponteiro.

21 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Note que essa implementação pontua apenas em caso de matches não penalizando indels e nem permitindo
mismatches.

Esse é a base do algoritmo Needleman-Wunsch [Needleman e Wunsch, 1970], proposto por Saul Neddleman
e Christian Wunsch na década de 1970. Trata-se de um algoritmo para alinhamento global par-a-par de
sequências. Há quatro tipos de algoritmos para alinhamento. Um alinhamento pode ser para-a-par ou múltiplo
e pode ser ao mesmo tempo global ou local. Seguem as definições desses tipos de alinhamentos.

Alinhamentos par-a-par são usadas para identificar regiões de similaridade que podem indicar relações
funcionais, estruturais e / ou evolutivas entre duas seqüências biológicas (proteína ou ácido nucleico).
—EMBL/EBI Web Site

Alinhamentos múltiplos (do inglês Multiple Sequence Alignment - MSA) é geralmente o alinhamento de
três ou mais sequências biológicas (proteína ou ácido nucleico) de comprimento similar. A partir da saída, a
homologia pode ser inferida e as relações evolutivas entre as seqüências estudadas.
—EMBL/EBI Web Site

Alinhamento global é um alinhamento de ponta a ponta das seqüências a alinhadas.


—EMBL/EBI Web Site

Alinhamento local encontra um ou mais alinhamentos que descrevem as regiões mais semelhantes nas
seqüências a serem alinhadas.
—EMBL/EBI Web Site

ALGORITMO DE SMITH-WATERMAN
O algoritmo de Needleman-Wunsch que acabamos de apresentar é um algoritmo de alinhamento global par-
a-par e busca similaridades entre duas sequências globalmente. Ele é útil quando a similaridade entre
sequências se estender por toda sua extensão como ocorre, por exemplo, em proteínas de uma mesma
família. Normalmente proteínas de uma família são conservadas, tem comprimentos próximos mesmo em
organismos tão diversos quanto moscas e seres humanos.

Entretanto, em diversas aplicações biológicas, isso não ocorre e alinhamentos entre subsequências de v e w
podem ter uma pontuação bem maior que a pontuação de v e w quando alinhadas globalmente. Isso
acontece, por exemplo, em proteínas que tem mais de um domínio. Os domínios são altamente conservados
mas a proteína por inteiro não o é. Então como podemos encontrar essas regiões conservadas e ignorar as
áreas de maior dissimilaridade. Em 1981, Temple Smith e Michael Waterman propuseram uma elegante
modificação no algoritmo de Needleman-Wunsch que resolve o alinhamento local e que ficou conhecido
como o algoritmo de Smith-Waterman [Smith e Waterman, 1981].

22 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

A figura a seguir ilustra a diferença conceitual entre um alinhamento global e local e ainda nos dá uma primeira
dica de como funciona o algoritmo de Smith-Waterman, ou seja, qual a modificação implementada para
conseguir o máximo alinhamento global entre subsequências de v e w. Essa figura foi extraída de [Jones e
Pevzner, 2004].

Problema do Alinhamento Local de Sequências

Encontre o melhor alinhamento local entre duas sequências

Entradas: Duas sequências, v e w, e uma matriz de pontuação δ


Saída: Subsequências de v e w para os quais o alinhamento global, conforme a matriz de pontuação
utilizada δ, é máximo entre todos os alinhamentos globais de subsequências de v e w.

Matrizes de pontuação
Matrizes para pontuar a similaridade entre sequências de DNA usualmente são definidas pelos parâmetros:

• Match (Μ)

• Mismatch (μ)

• Indel (σ)

23 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

No exemplo bastante simplificado que usamos na abordagem de alinhamento global o valor de Μ foi “+1”, μ e
σ foram de “0”. Para alinhamento de sequências de proteínas, compostas por um alfabeto de 20 possíveis
aminoácidos, usamos matrizes de pontuação δ. As matrizes mais comumente utilizadas são:
• PAM: Point Accepted Mutations, desenvolvida por Margareth Dayhoff [Dayhoff et al., 1978]
• BLOSUM: Block Substitution, desenvolvida por Steven e Joria Henikoff [Henikoff e Henikoff, 1992]
Mutações aleatórias em sequências de nucleotídeos podem provocar mudanças na sequência de
aminoácidos. Algumas dessas mutações podem não afetar a estrutura e a função de proteínas mas outras
podem ser muito relevantes afetando a habilidade de sobrevivência do organismo. Dessa forma, há
aminoácidos que são mais comumente mutados (ASN, ASP, GLU, SER) e outros raramente mutados (CYS e
TRP). Por exemplo, a probabilidade de se encontrar uma SER mutada por uma PHE é três vezes maior que
de se encontrar um TRP mutado por uma PHE. É esse tipo de conhecimento estatístico sobre as mutações
que ocorrem nas proteínas dos seres vivos que permitiram aos biólogos contraírem matrizes de pontuação
que nos permitem alinhar adequadamente sequências de proteínas. Uma matriz de pontuação δ(i, j) traz a
frequência na qual encontramos um aminoácido i substituído por um j.

Voltando ao Problema do Alinhamento Local de Sequências, Smith e Waterman, usando uma matriz de
substituição δ para pontuar dissimilaridades entre aminoácidos substituídos em duas sequências de proteínas
v e w, perceberam que, como uma pequena e simples modificação o algoritmo de Needleman-Wunsch
poderia ser usado para buscar o melhor alinhamento global entre duas subsequências de v e w, ou em outras
palavras, o máximo alinhamento local entre duas sequências.
Lembre que para resolver o LCS (alinhamento global), criamos uma matriz de programação dinâmica e a
preenchemos seguindo o seguinte critério:

si, j = max (si-1, j, si, j-1 e si-1, j-1+1) (desde que v[i] = w[j]):

Há variações desse critério que pontuam diferentemente para matches, mismatches e indels como, por
exemplo:

si, j = max (si-1, j -σ, si, j-1 -σ, si-1, j-1 -μ (desde que v[i] ≠ w[j])) e si-1, j-1+Μ (desde que v[i] = w[j])):

onde:
• Μ: é a pontuação de um match

• μ: é a penalidade de um mismatch

• σ: é a penalidade de um indel

Veja a seguir uma outra variação usando uma matriz de pontuação δ:

si, j = max (si-1, j +δ(vi, -), si, j-1 +δ(-, wj) e si-1, j-1 +δ(vi, wj)):

24 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

onde:
• δ(vi, -) e δ(vi, -): são as penalidade de indels (deleção e inserção, respectivamente).

• δ(vi, wj): pode ser a pontuação ou a penalidade dependendo se é um match ou mismatch

O Problema do Alinhamento Local foi resolvido simplesmente a substituição do critério anterior pelo seguinte:

si, j = max (0, si-1, j +δ(vi, -), si, j-1 +δ(-, wj) e si-1, j-1 +δ(vi, wj)):

onde:
• δ(vi, -) e δ(vi, -): são as penalidade de indels (deleção e inserção, respectivamente).

• δ(vi, wj): pode ser a pontuação ou a penalidade dependendo se é um match ou mismatch

Note que ele é idêntico ao critério anterior usado no alinhamento global exceto por adicionar “0" pontos no
caso de a pontuação ter se tornado negativa. É como se adicionássemos uma aresta de peso “0" no grid ou,
em outras palavras, conectássemos o nó fonte s0,0 a todos os outros no si,j do grid. Dessa forma, sempre que
um alinhamento se torna muito ruim (pontuação negativa), pode-se recomeçá-lo zerando a pontuação e
ignorando a subsequência inicial.

Outra modificação importante a ser destacada é que no alinhamento global, a pontuação do melhor
alinhamento sempre está no nó (ou célula) sm,n enquanto no alinhamento local buscamos a célula si,j de

25 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

pontuação máxima (que indica onde o alinhamento irá terminar nas sequências v e w). Dessa forma,
caracteres após (ou à direita) de i e j não farão parte do alinhamento local máximo de v e w.
Esses são as duas opções de alinhamento par-a-par de sequências. Conforme explicado, ambas retornam o
melhor alinhamento possível dado um esquema de pontuação escolhido. Pode ser interessante obter
também os k melhores alinhamentos não sobrepostos mas esse é outro problema que não abordaremos
nesse módulo.

E quanto aos alinhamentos múltiplos de sequências? Como eles são calculados? Poderiam os mesmos
algoritmos serem usados para esse novo propósito?

ALINHAMENTO MÚLTIPLO DE SEQUÊNCIAS


O nosso principal objetivo quando alinhamos sequências é identificar similaridades estruturais e funcionais
entre proteínas. Biologicamente, proteínas similares podem não apresentar alta similaridade de sequência mas
mesmo assim é um problema muito importante em bioinformática ser capaz de identificar essas similaridades
mesmo que fracas. Frequentemente, se temos duas sequências de baixa similaridade, falhamos em encontrar
essas similaridades em um alinhamento par-a-par. Entretanto, o alinhamento de diversas sequências de uma
família podem nos permitir encontrar similaridades que podem ser invisíveis em uma alinhamento par-a-par.

Se tivéssemos 3 sequências e desejássemos encontrar o alinhamento máximo entre elas, precisaríamos


construir uma matriz de programação dinâmica tridimensional tal como a ilustrada a seguir:

26 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

Recordamos o que aprendemos no módulo sobre análise de algoritmos, note que um algoritmo para construir
uma matriz bidimensional de programação dinâmica para resolver o alinhamento par-a-par de sequências é
O(n2). Para uma matriz tridimensional (alinhamento múltiplo de 3 sequências) seria O(n3) e, por indução, para
um alinhamento de k sequências seria O(nk). Como k é uma variável que representa o número de sequências
a serem alinhadas, esse processamento de alinhamento é proibitivo ou intratável visto que é exponencial.
Como discutido no módulo sobre análise de algoritmos, essa não deve ser a forma utilizada na prática para
resolver problemas de alinhamento de sequências.
Problemas exponenciais são resolvidos na prática através de heurísticas:

Heurísticas ou algoritmos aproximados são denominações para o algoritmos que fornecem soluções
sem um limite formal de qualidade, tipicamente avaliado empiricamente em termos de complexidade
(média) e qualidade das soluções.
—Wikipedia

Em computação, normalmente duas propriedades são extremamente desejáveis quando projetamos um


algoritmo:
• Um tempo de execução aceitável
• Uma solução ótima ou muito boa para um determinado problema.
Um algoritmo aproximado não cumpre uma dessas propriedades, podendo encontrar boas soluções a
maioria das vezes mas sem garantias de que sempre encontrará uma boa solução. Além disso, podem não
haver garantias de que executará em tempo aceitável todas as vezes. Normalmente, heurísticas são
desenvolvidas utilizando alguma informação ou intuição a respeito do problema e da sua estrutura para
resolvê-lo de forma mais rápida.
Grande parte dos problemas importantes em bioinformática são resolvidos por heurísticas. Isso pois a grande
maioria dos problemas relevantes em bioinformática são não polinomiais. Há diversos tipos de heurísticas
mas deixamos sua apresentação para um curso de algoritmos em bioinformática mais avançado.
Com relação ao alinhamento múltiplo de sequências, uma abordagem comum a diversos algoritmos
amplamente utilizada consiste em realizar todos os possíveis alinhamentos ótimos par-a-par e ir combinando-
os sucessivamente para construir um alinhamento múltiplo. Outra possibilidade é partir de um bom
alinhamento par-a-par e ir adicionando as sequências mais similares sucessivamente até obter o alinhamento
múltiplo desejado. Essa seria uma heurística gulosa progressiva. O famoso CLUSTAL [Higgins e Sharp, 1988]
utiliza essa abordagem. Note que ela é famosa abordagem “uma vez um gap, sempre um gap”. Alinhamentos
múltiplos podem ser bastante ruins dependendo do grau de dissimilaridade entre as sequências. Além disso,
por ser um processo heurístico, os diversos algoritmos de alinhamento múltiplo geram resultados bastante
diferentes e de qualidades bastante diversas.

Nesse módulo, discutimos idéias por trás do mais famoso e, sem dúvida, mais importante problema
computacional em bioinformática. Tentamos guiar o estudante para compreender as idéias que embasam a
concepção dos algoritmos, bem como de outros problemas clássicos em computação que originaram os
algoritmos que são usados para alinhamento de sequências. Adicionalmente, discutimos a complexidade

27 de 29
MÓDULO 4 - ALGORITMOS PARA BIOINFORMÁTICA

computacional desses métodos e mostramos que problemas similares como alinhamento par-a-par e múltiplo
podem ter custos tão distintos, sendo o primeiro bem resolvido e o último intratável e passível de solução
heurística. Apesar de não nos estendermos na abordagem a outros problemas na área, esperamos ter dado
uma idéia de como um bioinformata precisa conhecer profundamente os métodos que utiliza, suas entradas,
saídas, principais algoritmos e complexidade computacional. Esperamos ainda ter motivado os estudantes
das áreas biológicas a buscarem um conhecimento amplo, mesmo que mais horizontal, de algoritmos
clássicos em computação visto que eles podem ser úteis na construção de novas soluções em
bioinformática. Por fim, não desejamos que um bioinformata fique “reinventando a roda”, ou seja,
desenvolvendo novos algoritmos para problemas que já são bem resolvidos em computação por simples
desconhecimento do corpo de conhecimento da área. Um grande risco obviamente seria reinventar a roda
criando uma roda menos eficiente que as já utilizadas há décadas.
Orientamos o estudante interessado a iniciar seus estudos pelo livro de Thomas Cormen [Cormen, 2009] que
é um grande clássico da computação. Trata-se de um livro que dispõe de versão em português, bastante
didático e completo. Não se assuste com seu volume mas tenha a meta de conhecer pouco a pouco seu
conteúdo durante a construção de sua formação como bioinformata.

Referências
[Cormen, 2009] Cormen, Thomas H. Introduction to algorithms. MIT press, 2009.

[Dayhoff et al., 1978] Dayhoff, M. O., R. M. Schwartz e B. C. Orcutt. "22 A Model of Evolutionary Change in
Proteins." Atlas of protein sequence and structure. Vol. 5. National Biomedical Research Foundation Silver
Spring, MD, 1978. 345-352.

[Henikoff e Henikoff, 1992] Henikoff, Steven, and Jorja G. Henikoff. "Amino acid substitution matrices from
protein blocks." Proceedings of the National Academy of Sciences 89.22 (1992): 10915-10919.

[Higgins e Sharp, 1988] Higgins, Desmond G., and Sharp, Paul M.. "CLUSTAL: a package for performing
multiple sequence alignment on a microcomputer." Gene 73.1 (1988): 237-244.

[Jones e Pevzner, 2004] Jones, Neil C., e Pavel Pevzner. An introduction to bioinformatics algorithms. MIT
press, 2004.

[Needleman e Wunsch, 1970] Needleman, Saul B., and Christian D. Wunsch. "A general method applicable to
the search for similarities in the amino acid sequence of two proteins." Journal of molecular biology 48.3
(1970): 443-453.

[Smith e Waterman, 1981] Smith, Temple F., and Michael S. Waterman. "Identification of common molecular
subsequences." Journal of molecular biology147.1 (1981): 195-197.

28 de 29

Você também pode gostar