📜  洞穴-惠勒数据转换算法

📅  最后修改于: 2021-04-17 13:15:42             🧑  作者: Mango

什么是Burrows-Wheeler变换?
BWT是一种数据转换算法,它以使转换后的消息更具可压缩性的方式重组数据。从技术上讲,它是字符串字符的字典顺序可逆排列。在实现构成Unix压缩实用程序bzip2的基础的Burrows-Wheeler数据压缩算法时,这是要连续执行的三个步骤中的第一步。

为什么选择BWT?其背后的主要思想。
BWT的最重要应用是在生物科学中,其中基因组(用A,C,T,G字母写的长字符串)运行次数不多,但它们却有很多重复。
BWT的想法是构建一个数组,该数组的行都是字典顺序的输入字符串的循环移位,并返回该数组的最后一列,该列往往具有相同的字符。这样做的好处是,一旦字符被聚在一起,它们就会有效地进行排序,这可以使我们的字符串对于其他算法(如游程长度编码和霍夫曼编码)具有更高的可压缩性。
BWT的显着之处在于,这种特定的转换是可逆的,并且数据开销最小。

BWT算法涉及的步骤
让我们以“ banana $”一词为例。

  • 步骤1:形成给定文本的所有循环旋转。
    banana$ 
           $    b                        $banana 
        a           a                    a$banan
       Cyclic rotations    ---------->   na$bana
        n         n                      ana$ban 
              a                          nana$ba
                                         anana$b
    
  • 步骤2:下一步是按字典顺序对旋转进行排序。在字典上, “ $”符号被视为第一个字母,甚至在“ a”之前。
    banana$                    $banana
    $banana                    a$banan
    a$banan       Sorting      ana$ban
    na$bana      ---------->   anana$b 
    ana$ban    alphabetically  banana$
    nana$ba                    na$bana
    anana$b                    nana$ba
    
  • 步骤3:最后一列是我们作为BWT输出的内容。
    BWT(banana$) = annb$aa
    

例子:

为什么最后一列被视为BWT?

  1. 最后一列比其他任何列都有更好的符号聚类。
  2. 如果只有字符串的BWT,则可以完全恢复其余的循环旋转。其余的列不具有此特征,这在计算BWT的逆数时非常重要。

为什么在文本中嵌入“ $”符号?
即使我们的文本未与任何EOF字符(此处为“ $” )连接,我们也可以计算BWT。计算BWT的逆时会出现“ $”符号的含义。

实施方式

  1. 让我们实例化“ banana $”作为我们的input_text,并实例化字符数组bwt_arr作为我们的输出。
  2. 让我们获取“ banana $”的所有后缀,并计算它的suffix_arr来存储每个后缀的索引。
    0 banana$                6 $   
    1 anana$                 5 a$
    2 nana$      Sorting     3 ana$
    3 ana$     ---------->   1 anana$
    4 na$     alphabetically 0 banana$
    5 a$                     4 na$
    6 $                      2 nana$
    
  3. 遍历suffix_arr ,现在将添加到输出数组bwt_arr ,它是每次旋转的最后一个字符。
  4. 可以使用input_text [(suffix_arr [i] – 1 + n)%n]来计算从后缀数组中当前值表示的位置开始的input_text每次旋转的最后一个字符,其中n是元素中元素的数量。 suffix_arr
    bwt_arr[0] 
      = input_text[(suffix_arr[0] - 1 + 7) % 7] 
      = input_text[5] 
      = a
    bwt_arr[1] 
      = input_text[(suffix_arr[1] - 1 + 7) % 7] 
      = input_text[4] 
      = n
    

以下是上述实现方式的代码

// C program to find Burrows Wheeler transform
// of a given text
  
#include 
#include 
#include 
  
// Structure to store data of a rotation
struct rotation {
    int index;
    char* suffix;
};
  
// Compares the rotations and
// sorts the rotations alphabetically
int cmpfunc(const void* x, const void* y)
{
    struct rotation* rx = (struct rotation*)x;
    struct rotation* ry = (struct rotation*)y;
    return strcmp(rx->suffix, ry->suffix);
}
  
// Takes text to be transformed and its length as
// arguments and returns the corresponding suffix array
int* computeSuffixArray(char* input_text, int len_text)
{
    // Array of structures to store rotations and
    // their indexes
    struct rotation suff[len_text];
  
    // Structure is needed to maintain old indexes of
    // rotations after sorting them
    for (int i = 0; i < len_text; i++) {
        suff[i].index = i;
        suff[i].suffix = (input_text + i);
    }
  
    // Sorts rotations using comparison
    // function defined above
    qsort(suff, len_text, sizeof(struct rotation),
          cmpfunc);
  
    // Stores the indexes of sorted rotations
    int* suffix_arr
        = (int*)malloc(len_text * sizeof(int));
    for (int i = 0; i < len_text; i++)
        suffix_arr[i] = suff[i].index;
  
    // Returns the computed suffix array
    return suffix_arr;
}
  
// Takes suffix array and its size
// as arguments and returns the
// Burrows - Wheeler Transform of given text
char* findLastChar(char* input_text,
                   int* suffix_arr, int n)
{
    // Iterates over the suffix array to find
    // the last char of each cyclic rotation
    char* bwt_arr = (char*)malloc(n * sizeof(char));
    int i;
    for (i = 0; i < n; i++) {
        // Computes the last char which is given by
        // input_text[(suffix_arr[i] + n - 1) % n]
        int j = suffix_arr[i] - 1;
        if (j < 0)
            j = j + n;
  
        bwt_arr[i] = input_text[j];
    }
  
    bwt_arr[i] = '\0';
  
    // Returns the computed Burrows - Wheeler Transform
    return bwt_arr;
}
  
// Driver program to test functions above
int main()
{
    char input_text[] = "banana$";
    int len_text = strlen(input_text);
  
    // Computes the suffix array of our text
    int* suffix_arr
        = computeSuffixArray(input_text, len_text);
  
    // Adds to the output array the last char
    // of each rotation
    char* bwt_arr
        = findLastChar(input_text, suffix_arr, len_text);
  
    printf("Input text : %s\n", input_text);
    printf("Burrows - Wheeler Transform : %s\n",
           bwt_arr);
    return 0;
}
输出:
Input text : banana$
Burrows - Wheeler Transform : annb$aa

时间复杂度: O( n^2登录)。这是因为上面使用的方法来构建具有O( n^2由于O(nLogn)排序算法中的字符串比较需要O(n)时间,因此Logn)时间复杂度高。

锻炼:

  1. 在O(nLogn)时间计算后缀数组,然后实现BWT。
  2. 实现Burrows-Wheeler变换的逆函数。