Needleman-Wunsch Algorithm

Let's say you have two genetic sequences from two different individuals, and you want to know whether they represent the same gene. How would you go about it? You could let your computer do a simple string comparison, but then you would almost never get a match, even if the two nucleotide sequences do come from the same gene. That's because there is so much genetic variation, especially if the individuals come from two different species. What you need is a more sophisticated approach, one that will give you a good match even if the sequences have diverged in the course of evolution through mutations such as insertions, deletions or nucleotide changes.

This is what the Needleman-Wunsch algorithm does. It optimizes the alignment between two similar sequences, and assumes that the sequences are similar throughout. In other words, it is a global alignment algorithm. The Smith-Watermann algorithm is even more useful, because it doesn't assume that the sequences should be similar everywhere, but instead tries to line up sections of the sequences that give a good match. I implemented the Needleman-Wunsch Algorithm in C++ based on its description in Biological Sequence Analysis, published in 1998. The book is an excellent introduction to biological sequence analysis, including other dynamic programming algorithms, such as hidden Markov models.

C++ Source Code:

  • main.cpp
  • /*---------------------------------------------------------------
     *
     *   main.cpp for nw program.
     *
     *   Implements the Needleman-Wunsch algorithm
     *   for global nucleotide sequence alignment.
     *
     *   Rolf Muertter,  rolf@dslextreme.com
     *   9/5/2006
     *
     ---------------------------------------------------------------*/
    
    
    #include "nw.h"
    
    using namespace std;
    
    
    int  main( int argc, char ** argv )
    {
            char *  program = *argv ;
            bool    prm = false;
    
    
            if( argc < 3 )
            {
                    cerr << "\n   Usage:   " << program << " seq_1 seq_2 [-p]\n";
                    cerr << "\n   -p:       Print matrices\n";
                    cerr << "\n   Output:   alignment\n\n";
    
                    exit( 1 ) ;
            }                       
            
            // Sequences to be aligned
            string  seq_1   =  argv[ 1 ] ;
            string  seq_2   =  argv[ 2 ] ;
    
            if( argc == 4 )
            {
                    string  pr_arg  =  argv[ 3 ] ;
                    if( pr_arg == "-p" )  prm = true;   // Print matrices
            }                       
    
            #if DEBUG
                cout << "seq_1: " << seq_1 << endl;
                cout << "seq_2: " << seq_2 << endl;
                cout << "-p: " << prm << endl;
            #endif
    
            // Aligned sequences
            string  seq_1_al;
            string  seq_2_al;
    
            // Get alignment
            nw( seq_1, seq_2, seq_1_al, seq_2_al, prm ) ;   
    
            // Print alignment
            print_al( seq_1_al, seq_2_al ) ;        
    
            return  0 ;
    }
    


  • nw.cpp
  • /*-------------------------------------------
     * 
     *        nw.cpp for program nw
     * 
     -------------------------------------------*/
    
    #include "nw.h"
    
    using namespace std;
    
    
    int nw(                                                          
            string       seq_1,          /*  Needleman-Wunsch   */
            string       seq_2,          /*  algorithm for      */
            string&      seq_1_al,       /*  global alignment   */
            string&      seq_2_al,       /*  of nt sequence.    */
            bool         prm
          )
    {
            int  d = 2 ;                 /* gap penalty */
    
            int  L1 = seq_1.length();
            int  L2 = seq_2.length();
    
            // Dynamic programming matrix
            int ** F = new int * [ L2+1 ];
            for( int i = 0; i <= L2; i++ )  F[ i ] = new int [ L1 ];
    
            // Traceback matrix
            char ** traceback = new char * [ L2+1 ];
            for( int i = 0; i <= L2; i++ )  traceback[ i ] = new char [ L1 ];
    
            // Initialize traceback and F matrix (fill in first row and column)
            dpm_init( F, traceback, L1, L2, d );
    
            // Create alignment
            nw_align( F, traceback, seq_1, seq_2, seq_1_al, seq_2_al, d );
    
            #if DEBUG
                int  L_al = seq_1_al.length();
                cout << "Length after alignment: " << L_al << endl;
            #endif
    
            if( prm )
            {
                    cout << "\nDynamic programming matrix: " << "\n\n";
                    print_matrix( F, seq_1, seq_2 );
    
                    cout << "\nTraceback matrix: " << "\n\n";
                    print_traceback( traceback, seq_1, seq_2 );
    
                    cout << endl;
            }
    
            for( int i = 0; i <= L2; i++ )  delete F[ i ];
            delete[] F;
            for( int i = 0; i <= L2; i++ )  delete traceback[ i ];
            delete[] traceback;
    
            return  0 ;
    }
    
    
    void  dpm_init( int ** F, char ** traceback, int L1, int L2, int d )
    {
            F[ 0 ][ 0 ] =  0 ;
            traceback[ 0 ][ 0 ] = 'n' ;
    
            int i=0, j=0;
    
            for( j = 1; j <= L1; j++ )
            {
                    F[ 0 ][ j ] =  -j * d ;
                    traceback[ 0 ][ j ] =  '-' ;
            }
            for( i = 1; i <= L2; i++ )
            {
                    F[ i ][ 0 ] =  -i * d ;
                    traceback[ i ][ 0 ] =  '|' ;
            }
    }
    
    
    int nw_align(                  // Needleman-Wunsch algorithm
                  int **     F,
                  char **    traceback,
                  string     seq_1,
                  string     seq_2,
                  string&    seq_1_al,
                  string&    seq_2_al,
                  int        d         // Gap penalty
                )
    {
            int        k = 0, x = 0, y = 0;
            int        fU, fD, fL ;
            char       ptr, nuc ;
            int        i = 0, j = 0;
    
            const int  a =  2;   // Match
            const int  b = -1;   // Mismatch
    
            const int  s[ 4 ][ 4 ] = { { a, b, b, b },    /* substitution matrix */
                                       { b, a, b, b },
                                       { b, b, a, b },
                                       { b, b, b, a } } ;
    
            int  L1 = seq_1.length();
            int  L2 = seq_2.length();
    
            for( i = 1; i <= L2; i++ )
            {
                    for( j = 1; j <= L1; j++ )
                    {
                            nuc = seq_1[ j-1 ] ;
    
                            switch( nuc )
                            {
                                    case 'A':  x = 0 ;  break ;
                                    case 'C':  x = 1 ;  break ;
                                    case 'G':  x = 2 ;  break ;
                                    case 'T':  x = 3 ;
                            }
    
                            nuc = seq_2[ i-1 ] ;
    
                            switch( nuc )
                            {
                                    case 'A':  y = 0 ;  break ;
                                    case 'C':  y = 1 ;  break ;
                                    case 'G':  y = 2 ;  break ;
                                    case 'T':  y = 3 ;
                            }
    
                            fU = F[ i-1 ][ j ] - d ;
                            fD = F[ i-1 ][ j-1 ] + s[ x ][ y ] ;
                            fL = F[ i ][ j-1 ] - d ;
    
                            F[ i ][ j ] = max( fU, fD, fL, &ptr ) ;
    
                            traceback[ i ][ j ] =  ptr ;
                    }
            }
            i-- ; j-- ;
    
            while( i > 0 || j > 0 )
            {
                    switch( traceback[ i ][ j ] )
                    {
                            case '|' :      seq_1_al += '-' ; 
                                            seq_2_al += seq_2[ i-1 ] ; 
                                            i-- ;
                                            break ;
    
                            case '\\':      seq_1_al += seq_1[ j-1 ] ; 
                                            seq_2_al += seq_2[ i-1 ] ; 
                                            i-- ;  j-- ;
                                            break ;
    
                            case '-' :      seq_1_al += seq_1[ j-1 ] ; 
                                            seq_2_al += '-' ; 
                                            j-- ;
                    }
                    k++ ;
            }
            
            reverse( seq_1_al.begin(), seq_1_al.end() );
            reverse( seq_2_al.begin(), seq_2_al.end() );
    
            return  0 ;
    }
    
    
    int  max( int f1, int f2, int f3, char * ptr )
    {
            int  max = 0 ;
    
            if( f1 >= f2 && f1 >= f3 )  
            {
                    max = f1 ;
                    *ptr = '|' ;
            }
            else if( f2 > f3 )              
            {
                    max = f2 ;
                    *ptr = '\\' ;
            }
            else
            {
                    max = f3 ;
                    *ptr = '-' ;
            }
            
            return  max ;   
    }
    
    
    void  print_matrix( int ** F, string seq_1, string seq_2 )
    {
            int  L1 = seq_1.length();
            int  L2 = seq_2.length();
    
            cout << "        ";
            for( int j = 0; j < L1; j++ )
            {
                    cout << seq_1[ j ] << "   ";
            }
            cout << "\n  ";
    
            for( int i = 0; i <= L2; i++ )
            {
                    if( i > 0 )
                    {
                            cout << seq_2[ i-1 ] << " ";
                    }
                    for( int j = 0; j <= L1; j++ )
                    {
                            cout.width( 3 );
                            cout << F[ i ][ j ] << " ";
                    }
                    cout << endl;
            }
    }
    
    
    void  print_traceback( char ** traceback, string seq_1, string seq_2 )
    {
            int  L1 = seq_1.length();
            int  L2 = seq_2.length();
    
            cout << "    ";
            for( int j = 0; j < L1; j++ )
            {
                    cout << seq_1[ j ] << " ";
            }
            cout << "\n  ";
    
            for( int i = 0; i <= L2; i++ )
            {
                    if( i > 0 )
                    {
                            cout << seq_2[ i-1 ] << " ";
                    }
                    for( int j = 0; j <= L1; j++ )
                    {
                            cout << traceback[ i ][ j ] << " ";
                    }
                    cout << endl;
            }
    }
    
    
    void  print_al( string& seq_1_al, string& seq_2_al )
    {
            cout << seq_1_al << endl;
            cout << seq_2_al << endl;
    }
    
    


  • nw.h
  • /*
     * nw.h for program nw.
     *
     */
    
    #include <iostream>
    #include <string>
    #include <algorithm>
    #include <stdlib.h>
    #include <stdio.h>
    
    #define DEBUG 0
    
    using namespace std;
    
    
    extern int  nw( 
                     string, string, 
                     string&, string&,
                     bool
                  );
    
    extern int  nw_align( 
                          int **, char **,
                          string, string, 
                          string&, string&,
                          int 
                        );
    
    extern void  dpm_init        ( int **, char **, int, int, int );
    extern void  print_al        ( string&, string& );
    extern void  print_matrix    ( int ** const, string, string );
    extern void  print_traceback ( char ** const, string, string );
    extern int   max             ( int, int, int, char * );
    




### 回答1: Needleman-Wunsch算法是一种用于比对两条生物序列(如DNA或蛋白质序列)的算法。它采用了动态规划的思想,通过构建一个二维矩阵来计算两条序列之间的最佳比对方式。它可以计算出两条序列之间的最高相似度,并用这个相似度来推断进化关系。 ### 回答2: Needleman-Wunsch算法是一种经典的序列比对算法,被广泛应用于生物信息学领域和DNA/RNA/蛋白质序列的比对工作中。该算法的核心思想是通过动态规划的方法,找到两个序列之间的最佳比对方案。 算法的步骤如下: 1. 初始化一个二维矩阵,大小为两个序列长度加1。矩阵的第一行和第一列分别对应两个序列的每个字符。 2. 初始化第一行和第一列,即给每个元素赋予相应的惩罚分数。一般来说,匹配得分为正,不匹配和缺失的得分为负。 3. 根据相应的匹配规则,计算每个矩阵元素的得分。矩阵中的每个元素都表示该位置匹配到的最佳得分。 4. 通过回溯的方式,根据得分矩阵确定最佳比对方案。从得分矩阵的右下角开始,根据当前位置的得分和其周围位置的得分,决定向上、向左还是左上方向移动。 5. 根据比对方案,生成最佳比对序列。 Needleman-Wunsch算法具有以下特点: 1. 能够找到两个序列之间的全局最佳比对方案,即找到最大得分的比对方式。 2. 能够处理序列长度不等的情况,能够对缺失或插入的位置进行补全。 3. 对于大规模的序列比对,算法的时间复杂度较高,需要额外的计算资源。 4. 算法中的得分矩阵可以用于表示序列的相似性或差异性。 Needleman-Wunsch算法的应用广泛,例如在基因组学研究中,可以比对不同物种的基因组序列,寻找共同的基因功能区域。在药物设计中,可以比对蛋白质序列,寻找同源蛋白质并预测其结构和功能。此外,该算法还可以应用于DNA测序中,对测序结果进行比对和校正。 总之,Needleman-Wunsch算法是一种有效的序列比对算法,在生物信息学和相关领域具有重要的应用价值。 ### 回答3: Needleman-Wunsch算法是一种常见的序列比对算法,用于比较两个序列之间的相似性。它是由Saul Needleman和Christian Wunsch于1970年提出的,是一种全局比对算法,适用于字符串、蛋白质序列或DNA序列的比对。 需要进行比对的两个序列被放置在一个二维的矩阵中。算法根据预先定义的匹配得分、替换得分和惩罚值,计算出每个位置的得分。在计算的过程中,需要考虑序列间插入或删除字符的成本。 算法的具体步骤如下: 1. 初始化一个空的二维矩阵,矩阵的大小是两个序列的长度加一。 2. 在矩阵的边缘填充惩罚值。 3. 从矩阵的左上角开始,计算每个位置的得分。得分是根据上方、左方和左上方的得分和匹配情况计算的。 4. 根据得分确定最佳的替换、匹配或删除操作,并将对应的字符插入到比对结果中。 5. 重复步骤3和4,直到到达矩阵的右下角。 6. 根据得分矩阵构建最佳比对结果。 Needleman-Wunsch算法的时间复杂度为O(n^2),其中n是序列的长度。它可以找到两个序列之间的最佳比对结果,但可能会受限于较长序列的内存需求。虽然算法的计算量较大,但由于它的准确性和全局比对的能力,在生物信息学领域得到广泛应用,例如蛋白质结构的比对和进化树的构建等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值