原创: hxj7

之前几篇文章介绍了全局匹配以及局部匹配,本文介绍交叉匹配问题并给出代码。

交叉匹配

所谓交叉匹配(overlap alignment 或者叫 glocal alignment),就是两条序列中至少有一条的头部序列要参加比对并且至少有一条的尾部序列要参加比对。

一般而言,就是下面两种情形:

一种是两条序列有重叠的部分,但互不包含。比如x序列的头部与y序列的尾部匹配。

java 交叉组合 交叉搭配_生物信息

第二种是一条序列包含另一条序列,比如x序列包含y序列。

java 交叉组合 交叉搭配_算法_02

与全局联配、局部联配的区别

那么如何得到这种匹配的最高匹配得分呢?首先得弄清楚此类匹配与全局联配、局部联配的区别。
不同于全局匹配,交叉匹配中两端的序列可以不参与联配(或者说不罚分)。
不同于局部匹配,交叉匹配中某一条序列的头部必须参与联配且某一条序列的尾部必须参加联配。

交叉匹配的算法

假设x序列和y序列的长度分别是m和n,根据上面的比较可以得到解决交叉匹配问题的关键步骤(依然是利用得分矩阵):

  1. 设置F(0, 0) = 0。F(i, 0) = 0; i = 1, 2, …, m。F(0, j) = 0; j = 1, 2, …, n。
  2. 得分矩阵最后一行的最大值以及最后一列的最大值中的较大者为Fmax。
  3. 从Fmax所在单元开始回溯,到达i = 0的行(代表x序列的头部)或者j = 0的列(代表y序列的头部)为止。
    得分矩阵的计算同全局联配:

    图片引自《生物序列分析》
    具体的代码参见文末的 alnOverlap.c 代码段。

限定x头部与y尾部的交叉匹配

如果在给出交叉匹配问题的同时,限定x头部以及y尾部的序列必须参与联配,又该如何计算最高得分呢?

注意此时F(i,0)=i*gap。也就是说,x序列的头部联配空位要罚分了。

此时比对的最高得分就是最后一列的最大值。至于回溯其实也很简单,这个时候只需要从最后一列的最大值所在单元开始回溯,到i = 0的行结束。

或者,根据以下计算公式得到最高得分:

java 交叉组合 交叉搭配_序列比对_03


图片改编自《生物序列分析》

其中X(i)表示序列中x(i)不参与联配的情况下联配的最高得分,X(0) = 0。T是得分阈值。比对的最高得分就是X(m+1)。回溯方式是从X(m)(如果其值等于X(m+1))或者F(m,n)(如果其值等于X(m+1))开始,到X(0)或者F(0,k)结束。
这种限定条件下的交叉联配代码参见文末的 alnOverlaoX.c 代码段。

代码

首先是 alnOverlap.c 代码段

#include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define MAXSEQ 1000
    #define GAP_CHAR '-'
    #define UNMATCH_CHAR '.'
    #define max2(a, b) ((a) > (b) ? (a) : (b))
    
    // 对空位的罚分是线性的
    // 00000001  是否往左回溯一格
    // 00000010  是否往左上回溯一格
    // 00000100  是否往上回溯一格
    struct Unit {
        int W;     // F(i, j)的回溯指标,不同的bit代表不同的回溯方式
        float M;      // 得分矩阵第(i, j)这个单元的分值,即序列s(1,...,i)与序列r(1,...,j)比对的最高得分
    };
    typedef struct Unit *pUnit;
    
    void strUpper(char *s);
    float maxOfRow(pUnit* a, int n);
    float maxOfCol(pUnit** a, int col, int n);
    float getFScore(char a, char b);
    void printAlign(pUnit** a, const int i, const int j, char* s, char* r, char* saln, char* raln, int n);
    void align(char *s, char *r, float t);
    
    int main() {
        char s[MAXSEQ];
        char r[MAXSEQ];
        float t;
        printf("The 1st seq: ");
        scanf("%s", s);
        printf("The 2nd seq: ");
        scanf("%s", r);
        printf("T (threshold): ");
        scanf("%f", &t);
        align(s, r, t);
        return 0;
    }
    
    void strUpper(char *s) {
        while (*s != '\0') {
            if (*s >= 'a' && *s <= 'z') {
                *s -= 32;
            }
            s++;
        }
    }
    
    float maxOfRow(pUnit* a, int n) {
        int i;
        float max = a[0]->M;
        for (i = 1; i < n; i++)
            if (a[i]->M > max)
                max = a[i]->M;
        return max;
    }
    
    float maxOfCol(pUnit** a, int col, int n) {
        int i;
        float max = a[0][col]->M;
        for (i = 1; i < n; i++)
            if (a[i][col]->M > max)
                max = a[i][col]->M;
        return max;
    }
    
    // 替换矩阵:match分值为5,mismatch分值为-4
    // 数组下标是两个字符的ascii码减去65之后的和
    float FMatrix[] = {
        5, 0, -4, 0, 5, 0, -4, 0, -4, 0,
        0, 0, 5, 0, 0, 0, 0, 0, 0, -4,
        0, -4, 0, 0, 0, -4, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 5
    };
    
    float getFScore(char a, char b) {
        return FMatrix[a + b - 'A' - 'A'];
    }
    
    void printAlign(pUnit** a, const int i, const int j, char* s, char* r, char* saln, char* raln, int n) {
        int k;
        pUnit p = a[i][j];
        if (! i) {  // 到达s序列的头部
            for (k = j; k >= 1; k--, n++) { // r序列头部未匹配的部分
                saln[n] = UNMATCH_CHAR;
                raln[n] = r[k - 1];
            }
            for (k = n - 1; k >= 0; k--)
                printf("%c", saln[k]);
            printf("\n");
            for (k = n - 1; k >= 0; k--)
                printf("%c", raln[k]);
            printf("\n\n");
            return;
        } else if (! j) {  // 到达r序列的头部
            for (k = i; k >= 1; k--, n++) { // s序列头部未匹配的部分
                raln[n] = UNMATCH_CHAR;
                saln[n] = s[k - 1];
            }
            for (k = n - 1; k >= 0; k--)
                printf("%c", saln[k]);
            printf("\n");
            for (k = n - 1; k >= 0; k--)
                printf("%c", raln[k]);
            printf("\n\n");
            return;
        }
        if (p->W & 1) {
            saln[n] = GAP_CHAR;
            raln[n] = r[j - 1];
            printAlign(a, i, j - 1, s, r, saln, raln, n + 1);
        }
        if (p->W & 2) {
            saln[n] = s[i - 1];
            raln[n] = r[j - 1];
            printAlign(a, i - 1, j - 1, s, r, saln, raln, n + 1);        
        }
        if (p->W & 4) {
            saln[n] = s[i - 1];
            raln[n] = GAP_CHAR;
            printAlign(a, i - 1, j, s, r, saln, raln, n + 1);        
        }    
    }
    
    void align(char *s, char *r, float t) {
        int i, j, k;
        int m = strlen(s);
        int n = strlen(r);
        float gap = -2.5;     // 对空位的罚分
        float em;   // Fmax
        float tm[3];
        pUnit **aUnit;
        char* salign;
        char* ralign;
        // 初始化
        if ((aUnit = (pUnit **) malloc(sizeof(pUnit*) * (m + 1))) == NULL) {
            fputs("Error: Out of space!\n", stderr);
            exit(1);
        }
        for (i = 0; i <= m; i++) {
            if ((aUnit[i] = (pUnit *) malloc(sizeof(pUnit) * (n + 1))) == NULL) {
                fputs("Error: Out of space!\n", stderr);
                exit(1);     
            }
            for (j = 0; j <= n; j++) {
                if ((aUnit[i][j] = (pUnit) malloc(sizeof(struct Unit))) == NULL) {
                    fputs("Error: Out of space!\n", stderr);
                    exit(1);     
                }
                aUnit[i][j]->W = 0;
            }
        }
        for (i = 0; i <= m; i++)  // 头部为匹配的部分不罚分
            aUnit[i][0]->M = 0;
        for (j = 1; j <= n; j++)
            aUnit[0][j]->M = 0;
        // 将字符串都变成大写
        strUpper(s);
        strUpper(r);
        // 动态规划算法计算得分矩阵每个单元的分值
        for (i = 1; i <= m; i++) {
            // 计算F(i, j)
            for (j = 1; j <= n; j++) {
                tm[0] = aUnit[i][j - 1]->M + gap;
                tm[1] = aUnit[i - 1][j - 1]->M + getFScore(s[i - 1], r[j - 1]);
                tm[2] = aUnit[i - 1][j]->M + gap;
                aUnit[i][j]->M = max2(max2(tm[0], tm[1]), tm[2]);
                for (k = 0; k < 3; k++)
                    if (tm[k] == aUnit[i][j]->M)
                        aUnit[i][j]->W |= 1 << k;
            }
        }
        // 计算Fmax
        em = max2(maxOfRow(aUnit[m], n + 1), maxOfCol(aUnit, n, m + 1));
    /*
        // 打印得分矩阵
        for (i = 0; i <= m; i++) {
            for (j = 0; j <= n; j++)
                printf("%f ", aUnit[i][j]->M);
            printf("\n");
        }
    */
        printf("max score: %f\n", em);
        // 打印最优比对结果,如果有多个,全部打印
        // 递归法
        if (em < t) {
            fputs("No seq aligned.\n", stdout);
        } else {
            if ((salign = (char*) malloc(sizeof(char) * (m + n))) == NULL) {
                fputs("Error: Out of space!\n", stderr);
                exit(1);
            }
            if ((ralign = (char*) malloc(sizeof(char) * (m + n))) == NULL) {
                fputs("Error: Out of space!\n", stderr);
                exit(1);
            }
            for (j = 1; j <= n; j++) {  // 从s序列的尾部开始回溯
                if (em == aUnit[m][j]->M) {
                    for (i = 0, k = n; k > j; i++, k--) {
                        salign[i] = UNMATCH_CHAR;
                        ralign[i] = r[k - 1];
                    }
                    printAlign(aUnit, m, j, s, r, salign, ralign, i);
                }
            }
            for (i = 1; i <= m; i++) {  // 从r序列的尾部开始回溯
                if (em == aUnit[i][n]->M) {
                    for (j = 0, k = m; k > i; j++, k--) {
                        ralign[j] = UNMATCH_CHAR;
                        salign[j] = s[k - 1];
                    }
                    printAlign(aUnit, i, n, s, r, salign, ralign, j);
                }
            }
            // 释放内存
            free(salign);
            free(ralign);
        }
        for (i = 0; i <= m; i++) {
            for (j = 0; j <= n; j++)
                free(aUnit[i][j]);
            free(aUnit[i]);
        }
        free(aUnit);
    }

然后是 alnOverlaoX.c 代码段

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSEQ 1000
#define GAP_CHAR '-'
#define UNALIGN_CHAR '.'
#define max2(a, b) ((a) > (b) ? (a) : (b))

// 对空位的罚分是线性的
// 00000001  是否往左回溯一格
// 00000010  是否往左上回溯一格
// 00000100  是否往上回溯一格
struct MUnit {
    int W;   // F(i, j)的回溯指标,不同的bit代表不同的回溯方式
    float M; // 得分矩阵第(i, j)这个单元的分值,即序列s(1,...,i)与序列r(1,...,j)比对的最高得分
};
typedef struct MUnit *pMUnit;

// 00000001  X(i - 1)
// 00000010  M(i - 1, n) - t
struct XUnit { // 表示X(i)不参与联配
    int W;
    float M;
};
typedef struct XUnit *pXUnit;

void strUpper(char *s);
float getFScore(char a, char b);
void printMAlign(pMUnit **a, const int i, const int j, char *s, char *r, char *saln, char *raln, int n);
void printXAlign(pMUnit **am, pXUnit *ax, const int i, const int n, char *s, char *r, char *saln, char *raln, int l);
void align(char *s, char *r, float t);

int main() {
    char s[MAXSEQ];
    char r[MAXSEQ];
    float t;
    printf("The 1st seq: ");
    scanf("%s", s);
    printf("The 2nd seq: ");
    scanf("%s", r);
    printf("T (threshold): ");
    scanf("%f", &t);
    align(s, r, t);
    return 0;
}

void strUpper(char *s) {
    while (*s != '\0') {
        if (*s >= 'a' && *s <= 'z') {
            *s -= 32;
        }
        s++;
    }
}

// 替换矩阵:match分值为5,mismatch分值为-4
// 数组下标是两个字符的ascii码减去65之后的和
float FMatrix[] = {
    5, 0, -4, 0, 5, 0, -4, 0, -4, 0,
    0, 0, 5, 0, 0, 0, 0, 0, 0, -4,
    0, -4, 0, 0, 0, -4, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 5
};

float getFScore(char a, char b) {
    return FMatrix[a + b - 'A' - 'A'];
}

void printMAlign(pMUnit **a, const int i, const int j, char *s, char *r, char *saln, char *raln, int n) {
    int k;
    pMUnit p = a[i][j];
    if (!i) { // 保证序列s的每个字符都比对上
        for (k = j; k >= 1; k--, n++) { // r序列头部未匹配的部分
            saln[n] = UNALIGN_CHAR;
            raln[n] = r[k - 1];
        }
        for (k = n - 1; k >= 0; k--)
            printf("%c", saln[k]);
        printf("\n");
        for (k = n - 1; k >= 0; k--)
            printf("%c", raln[k]);
        printf("\n\n");
        return;
    }
    if (!j) { // 到达r序列的头部
        for (k = i; k >= 1; k--, n++) { // s序列头部未匹配的部分
            raln[n] = GAP_CHAR;
            saln[n] = s[k - 1];
        }
        for (k = n - 1; k >= 0; k--)
            printf("%c", saln[k]);
        printf("\n");
        for (k = n - 1; k >= 0; k--)
            printf("%c", raln[k]);
        printf("\n\n");
        return;
    }
    if (p->W & 1) { // 向左回溯一格
        saln[n] = GAP_CHAR;
        raln[n] = r[j - 1];
        printMAlign(a, i, j - 1, s, r, saln, raln, n + 1);
    }
    if (p->W & 2) { // 向左上回溯一格
        saln[n] = s[i - 1];
        raln[n] = r[j - 1];
        printMAlign(a, i - 1, j - 1, s, r, saln, raln, n + 1);
    }
    if (p->W & 4) { // 向上回溯一格
        saln[n] = s[i - 1];
        raln[n] = GAP_CHAR;
        printMAlign(a, i - 1, j, s, r, saln, raln, n + 1);
    }
}

void printXAlign(pMUnit **am, pXUnit *ax, const int i, const int n, char *s, char *r, char *saln, char *raln, int l) {
    int k;
    if (!i) {
        for (k = l - 1; k >= 0; k--)
            printf("%c", saln[k]);
        printf("\n");
        for (k = l - 1; k >= 0; k--)
            printf("%c", raln[k]);
        printf("\n\n");
        return;
    }
    saln[l] = s[i - 1];
    raln[l] = UNALIGN_CHAR;
    if (ax[i]->W & 1) // X(i - 1)
        printXAlign(am, ax, i - 1, n, s, r, saln, raln, l + 1);
    if (ax[i]->W & 2) // M(i - 1, n)
        printMAlign(am, i - 1, n, s, r, saln, raln, l + 1);
}

void align(char *s, char *r, float t) {
    int i, j, k;
    int m = strlen(s);
    int n = strlen(r);
    float gap = -2.5; // 对空位的罚分
    float em;         // X(m + 1)
    float tm[3];
    pMUnit **aMUnit;
    pXUnit *aXUnit;
    char *salign;
    char *ralign;
    // 初始化
    if ((aMUnit = (pMUnit **)malloc(sizeof(pMUnit *) * (m + 1))) == NULL) {
        fputs("Error: Out of space!\n", stderr);
        exit(1);
    }
    for (i = 0; i <= m; i++) {
        if ((aMUnit[i] = (pMUnit *)malloc(sizeof(pMUnit) * (n + 1))) == NULL) {
            fputs("Error: Out of space!\n", stderr);
            exit(1);
        }
        for (j = 0; j <= n; j++) {
            if ((aMUnit[i][j] = (pMUnit)malloc(sizeof(struct MUnit))) == NULL) {
                fputs("Error: Out of space!\n", stderr);
                exit(1);
            }
            aMUnit[i][j]->W = 0;
        }
    }
    for (i = 0; i <= m; i++)
        aMUnit[i][0]->M = i * gap;
    for (j = 1; j <= n; j++)
        aMUnit[0][j]->M = 0;
    if ((aXUnit = (pXUnit *)malloc(sizeof(pXUnit) * (m + 1))) == NULL) {
        fputs("Error: Out of space!\n", stderr);
        exit(1);
    }
    for (i = 0; i <= m; i++) {
        if ((aXUnit[i] = (pXUnit)malloc(sizeof(struct XUnit))) == NULL) {
            fputs("Error: Out of space!\n", stderr);
            exit(1);
        }
        aXUnit[i]->W = 0;
    }
    aXUnit[0]->M = 0;
    // 将字符串都变成大写
    strUpper(s);
    strUpper(r);
    // 动态规划算法计算得分矩阵每个单元的分值
    for (i = 1; i <= m; i++) {
        // 计算X(i)
        aXUnit[i]->M = max2(aXUnit[i - 1]->M, aMUnit[i - 1][n]->M - t);
        if (aXUnit[i]->M == aXUnit[i - 1]->M)
            aXUnit[i]->W |= 1;
        if (aXUnit[i]->M == aMUnit[i - 1][n]->M - t)
            aXUnit[i]->W |= 2;
        // 计算F(i, j)
        for (j = 1; j <= n; j++) {
            tm[0] = aMUnit[i][j - 1]->M + gap;
            tm[1] = aMUnit[i - 1][j - 1]->M + getFScore(s[i - 1], r[j - 1]);
            tm[2] = aMUnit[i - 1][j]->M + gap;
            aMUnit[i][j]->M = max2(max2(tm[0], tm[1]), tm[2]);
            for (k = 0; k < 3; k++)
                if (tm[k] == aMUnit[i][j]->M)
                    aMUnit[i][j]->W |= 1 << k;
        }
    }
    // 计算X(m + 1, 0)
    em = max2(aXUnit[m]->M, aMUnit[m][n]->M - t);
    /*
        // 打印得分矩阵
        for (i = 0; i <= m; i++) {
            for (j = 0; j <= n; j++)
                printf("%f ", aMUnit[i][j]->M);
            printf("\n");
        }
    */
    printf("max score: %f\n", em);
    // 打印最优比对结果,如果有多个,全部打印
    // 递归法
    if (em == 0) {
        fputs("No seq aligned.\n", stdout);
    } else {
        if ((salign = (char *)malloc(sizeof(char) * (m + n))) == NULL) {
            fputs("Error: Out of space!\n", stderr);
            exit(1);
        }
        if ((ralign = (char *)malloc(sizeof(char) * (m + n))) == NULL) {
            fputs("Error: Out of space!\n", stderr);
            exit(1);
        }
        if (em == aXUnit[m]->M)
            printXAlign(aMUnit, aXUnit, m, n, s, r, salign, ralign, 0);
        if (em == aMUnit[m][n]->M - t)
            printMAlign(aMUnit, m, n, s, r, salign, ralign, 0);
        // 释放内存
        free(salign);
        free(ralign);
    }
    for (i = 0; i <= m; i++) {
        for (j = 0; j <= n; j++)
            free(aMUnit[i][j]);
        free(aMUnit[i]);
        free(aXUnit[i]);
    }
    free(aMUnit);
    free(aXUnit);
}