Algoritmo Needleman-Wunsch

El algoritmo de Needleman-Wunsch sirve para realizar alineamientos globales de dos secuencias. Se suele utilizar en el ámbito de la bioinformática para alinear secuencias de proteínas o de ácidos nucleicos. Fue propuesto por primera vez en 1970, por Saul Needleman y Christian Wunsch. Se trata de un ejemplo típico de programación dinámica. El algoritmo funciona del mismo modo independientemente de la complejidad o longitud de las secuencias y garantiza la obtención del mejor alineamiento.[1]

Las dos secuencias a alinear, llamadas A y B en los ejemplos, de longitud | A | = m {\displaystyle |A|=m} y | B | = n {\displaystyle |B|=n} , están formadas por elementos de un alfabeto finito de símbolos. El algoritmo necesita saber qué símbolos son diferentes entre sí y cuáles son iguales. Podemos utilizar una matriz cuadrada (S) para este propósito, en la que cada elemento S i j {\displaystyle S_{ij}} indique la similitud entre los elementos i y j del alfabeto usado. Si nuestro alfabeto de símbolos no fuese finito, en vez de una matriz podríamos usar una función R 2 R {\displaystyle R^{2}\rightarrow R} que tuviese como parámetros ambos símbolos a comparar y cuya salida fuese la similitud entre ambos. También se necesita otro parámetro (d) que nos indique cómo vamos a valorar que un símbolo no quede alineado con otro y que en su lugar se utilice un hueco.

Por ejemplo podemos definir la siguiente matriz:

A G C T A 10 1 3 4 G 1 7 5 3 C 3 5 9 0 T 4 3 0 8 {\displaystyle {\begin{array}{ccccc}-&A&G&C&T\\A&10&-1&-3&-4\\G&-1&7&-5&-3\\C&-3&-5&9&0\\T&-4&-3&0&8\end{array}}}

Y entonces el siguiente alineamiento:

AGACTAGTTAC
CGA---GACGT

con una penalización por hueco de d = 5 {\displaystyle d=-5} nos devolvería como solución óptima:

S ( A , C ) + S ( G , G ) + S ( A , A ) + 3 × d + S ( G , G ) + S ( T , A ) + S ( T , C ) + S ( A , G ) + S ( C , T ) = 3 + 7 + 10 3 × 5 + 7 + 4 + 0 + 1 + 0 = 1 {\displaystyle S(A,C)+S(G,G)+S(A,A)+3\times d+S(G,G)+S(T,A)+S(T,C)+S(A,G)+S(C,T)=-3+7+10-3\times 5+7+-4+0+-1+0=1}

Para determinar la puntuación óptima y poder reconstruir el alineamiento que devolvería esa puntuación se necesita otra matriz, F, que almacena los resultados parciales de cada posible alineamiento. Las dimensiones de la matriz F son el número de elementos en la secuencia A y el de B ( | A | × | B | {\displaystyle |A|\times |B|} ).

En cada iteración del algoritmo recibe valor un elemento de la matriz F. El valor que recibe el elemento F i j {\displaystyle F_{ij}} representa la puntuación obtenida al alinear de forma óptima los primeros i elementos de A y los primeros j de B. Cuando el algoritmo termine, el último elemento de F ( F m n {\displaystyle F_{mn}} , con m = | A | {\displaystyle m=|A|} y n = | B | {\displaystyle n=|B|} ) contendrá la puntuación para el alineamiento óptimo de ambas secuencias.

  Inicio del algoritmo:
  
  
    
      
        
          F
          
            0
            j
          
        
        =
        d
        
        j
      
    
    {\displaystyle F_{0j}=d*j}
  

  
  
    
      
        
          F
          
            i
            0
          
        
        =
        d
        
        i
      
    
    {\displaystyle F_{i0}=d*i}
  

  Recursión para obtener el siguiente elemento de forma óptima:
  
  
    
      
        
          F
          
            i
            j
          
        
        =
        max
        (
        
          F
          
            i
            
            1
            ,
            j
            
            1
          
        
        +
        S
        (
        
          A
          
            i
          
        
        ,
        
          B
          
            j
          
        
        )
        ,
        
          F
          
            i
            ,
            j
            
            1
          
        
        +
        d
        ,
        
          F
          
            i
            
            1
            ,
            j
          
        
        +
        d
        )
      
    
    {\displaystyle F_{ij}=\max(F_{i-1,j-1}+S(A_{i},B_{j}),F_{i,j-1}+d,F_{i-1,j}+d)}
  

La matriz F se calcula con el siguiente algoritmo:

   for i=0 to length(A)-1
     F(i,0) <- d*i
   for j=0 to length(B)-1
     F(0,j) <- d*j
   for i=1 to length(A)
     for j = 1 to length(B)
     {
       Choice1 <- F(i-1,j-1) + S(A(i), B(j))
       Choice2 <- F(i-1, j) + d
       Choice3 <- F(i, j-1) + d
       F(i,j) <- max(Choice1, Choice2, Choice3)
     }

Cuando el algoritmo acaba tenemos calculada la matriz F; el resultado es la puntuación devuelta por el mejor alineamiento posible, de acuerdo a los parámetros que hemos definido. Para obtener la secuencia se necesita ejecutar el siguiente algoritmo, que hace uso de la matriz F. Este algoritmo comienza por el último elemento, F m n {\displaystyle F_{mn}} , y va retrocediendo hasta llegar a un elemento de la primera fila o la primera columna de F. En cada paso se comparan 3 elementos de F para ver cuál de ellos es el que se ha seguido en la solución óptima. Para cada F i j {\displaystyle F_{ij}} debemos comparar F i 1 , j , F i , j 1 {\displaystyle F_{i-1,j},F_{i,j-1}} y F i 1 , j 1 {\displaystyle F_{i-1,j-1}} . Si el elemento usado es F i 1 , j {\displaystyle F_{i-1,j}} , entonces A i {\displaystyle A_{i}} se ha alineado con un hueco; si es F i , j 1 {\displaystyle F_{i,j-1}} , entonces B i {\displaystyle B_{i}} se ha alineado con un hueco; y si no, si el elemento elegido es F i 1 , j 1 {\displaystyle F_{i-1,j-1}} , los elementos A i {\displaystyle A_{i}} y B i {\displaystyle B_{i}} han sido alineados. Es importante destacar que el que dos elementos sean alineados no implica necesariamente que sean iguales; significa que entre esa posibilidad, alinear con huecos o alinear símbolos diferentes, esa era la mejor opción. El pseudo-algoritmo que permite obtener el alineamiento correcto es el siguiente:

   AlignmentA <- ""
   AlignmentB <- ""
   i <- length(A) 
   j <- length(B)
   while (i > 0 AND j > 0)
   {
     Score <- F(i,j)
     ScoreDiag <- F(i - 1, j - 1)
     ScoreUp <- F(i, j - 1)
     ScoreLeft <- F(i - 1, j)
     if (Score == ScoreDiag + S(A(i), B(j)))
     {
       AlignmentA <- A(i-1) + AlignmentA
       AlignmentB <- B(j-1) + AlignmentB
       i <- i - 1
       j <- j - 1
     }
     else if (Score == ScoreLeft + d)
     {
       AlignmentA <- A(i-1) + AlignmentA
       AlignmentB <- "-" + AlignmentB
       i <- i - 1
     }
     otherwise (Score == ScoreUp + d)
     {
       AlignmentA <- "-" + AlignmentA
       AlignmentB <- B(j-1) + AlignmentB
       j <- j - 1
     }
   }
   while (i > 0)
   {
     AlignmentA <- A(i-1) + AlignmentA
     AlignmentB <- "-" + AlignmentB
     i <- i - 1
   }
   while (j > 0)
   {
     AlignmentA <- "-" + AlignmentA
     AlignmentB <- B(j-1) + AlignmentB
     j <- j - 1
   }

Se puede demostrar formalmente que tanto el tiempo de ejecución como el espacio necesario para ejecutar el algoritmo son de orden O(nm). Para alguna aplicaciones, sobre todo en bioinformática, el requerimiento de espacio es prohibitivo, puesto que se alinean secuencias muy largas. Existe una optimización de este algoritmo, denominada algoritmo de Hirschberg, que solo necesita espacio del orden O(m+n), pero a costa de incrementar el tiempo de computación.

Sitios externos

  • Implementación del algoritmo en Java
  • Otra implementación en Java

Véase también

Referencias

  1. Needleman, S.B. and Wunsch, C.D. (1970). «A general method applicable to the search for similarities in the amino acid sequence of two proteins». Journal of molecular biology (Elsevier) 48 (3): 443-453. 
  • A general method applicable to the search for similarities in the amino acid sequence of two proteins. S.B. Needleman, C.D. Wunsch (1970) J. Mol. Biol. 48(3):443-453.
Control de autoridades
  • Proyectos Wikimedia
  • Wd Datos: Q583546
  • Commonscat Multimedia: Needleman–Wunsch algorithm / Q583546

  • Wd Datos: Q583546
  • Commonscat Multimedia: Needleman–Wunsch algorithm / Q583546