Invertir las madrigueras – Wheeler Transform

Prerrequisito: Burrows – Algoritmo de transformación de datos de Wheeler 

¿Por qué inversa de BWT? La idea principal detrás de esto: 

1. Lo notable del algoritmo BWT es que esta transformación en particular es invertible con una sobrecarga de datos mínima. 

2. Calcular el inverso de BWT es deshacer el BWT y recuperar la string original. El método ingenuo de implementar este algoritmo se puede estudiar desde aquí . El enfoque ingenuo requiere mucha velocidad y memoria y requiere que almacenemos |texto| rotaciones cíclicas de la string |texto|. 

3. Analicemos un algoritmo más rápido en el que solo tenemos dos cosas: 

  1. bwt_arr[] que es la última columna de la lista de rotaciones ordenadas dada como “annb$aa”
  2. ‘x’, que es el índice de fila en el que aparece nuestra string original «banana$» en la lista de rotaciones ordenadas. Podemos ver que ‘x’ es 4 en el siguiente ejemplo.
 Row Index    Original Rotations    Sorted Rotations
 ~~~~~~~~~    ~~~~~~~~~~~~~~~~~~    ~~~~~~~~~~~~~~~~
    0             banana$               $banana
    1             anana$b               a$banan
    2             nana$ba               ana$ban
    3             ana$ban               anana$b
   *4             na$bana               banana$
    5             a$banan               na$bana
    6             $banana               nana$ba

4. Una observación importante: si la j-ésima rotación original (que es la rotación original desplazada j caracteres a la izquierda) es la i-ésima fila en el orden ordenado, entonces l_shift[i] registra en el orden ordenado donde (j+1)st original aparece la rotación. Por ejemplo, la rotación original 0 «banana$» es la fila 4 del orden ordenado, y dado que l_shift[4] es 3, la siguiente rotación original «anana$b» es la fila 3 del orden ordenado.

Row Index  Original Rotations  Sorted Rotations l_shift 
~~~~~~~~~ ~~~~~~~~~~~~~~~~~~  ~~~~~~~~~~~~~~~~  ~~~~~~~
   0           banana$         $banana           4
   1           anana$b         a$banan           0
   2           nana$ba         ana$ban           5
   3           ana$ban         anana$b           6
  *4           na$bana         banana$           3
   5           a$banan         na$bana           1
   6           $banana         nana$ba           2

5. Nuestro trabajo es deducir l_shift[] de la información disponible que es bwt_arr[] y ‘x’ y con su ayuda calcular el inverso de BWT. ¿Cómo calcular l_shift[] ? 1. Sabemos BWT que es «annb$aa» . Esto implica que conocemos todos los caracteres de nuestra string original, aunque estén permutados en el orden incorrecto. 2. Al ordenar bwt_arr[] , podemos reconstruir la primera columna de la lista de rotaciones ordenadas y la llamamos sorted_bwt[] .

 Row Index    Sorted Rotations   bwt_arr    l_shift  
 ~~~~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~    ~~~~~~~   
     0         $  ?  ?  ?  ?  ?  a             4
     1         a  ?  ?  ?  ?  ?  n
     2         a  ?  ?  ?  ?  ?  n
     3         a  ?  ?  ?  ?  ?  b
    *4         b  ?  ?  ?  ?  ?  $             3
     5         n  ?  ?  ?  ?  ?  a
     6         n  ?  ?  ?  ?  ?  a

3. Dado que ‘$’ aparece solo una vez en la string ‘sorted_bwt[]’ y las rotaciones se forman mediante el ajuste cíclico, podemos deducir que l_shift[0] = 4. De manera similar, ‘b’ aparece una vez, por lo que podemos deducir que l_desplazamiento[4] = 3. 

4. Pero, debido a que ‘n’ aparece dos veces, parece ambiguo si l_shift[5] = 1 y l_shift[6] = 2 o si l_shift[5] = 2 y l_shift[6] = 1. 

5. La regla para resolver esta ambigüedad es que si las filas i y j comienzan con la misma letra e i<j, entonces l_shift[i] < l_shift[j] . Esto implica l_shift[5] = 1 y l_shift[6] =2. Continuando de manera similar, l_shift[] se calcula de la siguiente manera.

 Row Index    Sorted Rotations   bwt_arr    l_shift  
 ~~~~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~    ~~~~~~~   
     0         $  ?  ?  ?  ?  ?  a             4
     1         a  ?  ?  ?  ?  ?  n             0
     2         a  ?  ?  ?  ?  ?  n             5
     3         a  ?  ?  ?  ?  ?  b             6
    *4         b  ?  ?  ?  ?  ?  $             3
     5         n  ?  ?  ?  ?  ?  a             1
     6         n  ?  ?  ?  ?  ?  a             2

¿Por qué es válida la regla de resolución de ambigüedades? 

  1. Las rotaciones se ordenan de tal manera que la fila 5 es lexicográficamente menor que la fila 6. 
  2. Por lo tanto, los cinco caracteres desconocidos en la fila 5 deben ser menores que los cinco caracteres desconocidos en la fila 6 (ya que ambos comienzan con ‘n’ ). 
  3. También sabemos que entre las dos filas que terminan en ‘n’ , la fila 1 es más baja que la fila 2. 
  4. Pero, los cinco caracteres desconocidos en las filas 5 y 6 son precisamente los primeros cinco caracteres en las filas 1 y 2 o esto contradiría el hecho de que las rotaciones fueron ordenadas. 
  5. Por lo tanto, l_shift[5] = 1 y l_shift[6] = 2. 

Forma de implementación: 

1. Ordenar BWT: Usando qsort() , organizamos los caracteres de bwt_arr[] en orden ordenado y los almacenamos en sorted_arr[]

2. Calcule l_shift[]: 

i. Tomamos una array de punteros struct node *arr[] , cada uno de los cuales apunta a una lista enlazada. 

ii. Convirtiendo cada carácter distinto de bwt_arr[] en un Node principal de una lista vinculada, agregamos Nodes a la lista vinculada cuya parte de datos contiene el índice en el que aparece ese carácter en bwt_arr[] .

   i        *arr[128]           Linked Lists
~~~~~~~~~    ~~~~~~~~~      ~~~~~~~~~~~~~~~~~~~~~~  
   37          $     ----->    4 ->  NULL
   97          a     ----->    0 -> 5 -> 6 -> NULL
   110         n     ----->    1 -> 2 -> NULL
   98          b     ----->    3 -> NULL

iii. Haciendo caracteres distintos de sorted_bwt[] encabezados de listas enlazadas, recorremos listas enlazadas y obtenemos los valores correspondientes de l_shift[] .

     int[] l_shift = { 4, 0, 5, 6, 3, 1, 2 };

3. Iterando la longitud de la string, decodificamos BWT con x = l_shift[x] y generamos bwt_arr[x].

     x = l_shift[4] 
     x = 3
     bwt_arr[3] = 'b'

     x = l_shift[3] 
     x = 6
     bwt_arr[6] = 'a'

Ejemplos:

Input : annb$aa // Burrows - Wheeler Transform
        4 // Row index at which original message 
          // appears in sorted rotations list 
Output : banana$

Input : ard$rcaaaabb
        3
Output : abracadabra$

El siguiente es el código C para la forma de implementación explicada anteriormente: 

C

// C program to find inverse of Burrows
// Wheeler transform
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
// Structure to store info of a node of
// linked list
struct node {
    int data;
    struct node* next;
};
 
// Compares the characters of bwt_arr[]
// and sorts them alphabetically
int cmpfunc(const void* a, const void* b)
{
    const char* ia = (const char*)a;
    const char* ib = (const char*)b;
    return strcmp(ia, ib);
}
 
// Creates the new node
struct node* getNode(int i)
{
    struct node* nn =
        (struct node*)malloc(sizeof(struct node));
    nn->data = i;
    nn->next = NULL;
    return nn;
}
 
// Does insertion at end in the linked list
void addAtLast(struct node** head, struct node* nn)
{
    if (*head == NULL) {
        *head = nn;
        return;
    }
    struct node* temp = *head;
    while (temp->next != NULL)
        temp = temp->next;
    temp->next = nn;
}
 
// Computes l_shift[]
void* computeLShift(struct node** head, int index,
                    int* l_shift)
{
    l_shift[index] = (*head)->data;
    (*head) = (*head)->next;
}
 
void invert(char bwt_arr[])
{
    int i,len_bwt = strlen(bwt_arr);
    char* sorted_bwt = (char*)malloc(len_bwt * sizeof(char));
    strcpy(sorted_bwt, bwt_arr);
    int* l_shift = (int*)malloc(len_bwt * sizeof(int));
 
    // Index at which original string appears
    // in the sorted rotations list
    int x = 4;
 
    // Sorts the characters of bwt_arr[] alphabetically
    qsort(sorted_bwt, len_bwt, sizeof(char), cmpfunc);
 
    // Array of pointers that act as head nodes
    // to linked lists created to compute l_shift[]
    struct node* arr[128] = { NULL };
 
    // Takes each distinct character of bwt_arr[] as head
    // of a linked list and appends to it the new node
    // whose data part contains index at which
    // character occurs in bwt_arr[]
    for (i = 0; i < len_bwt; i++) {
        struct node* nn = getNode(i);
        addAtLast(&arr[bwt_arr[i]], nn);
    }
 
    // Takes each distinct character of sorted_arr[] as head
    // of a linked list and finds l_shift[]
    for (i = 0; i < len_bwt; i++)
        computeLShift(&arr[sorted_bwt[i]], i, l_shift);
 
    printf("Burrows - Wheeler Transform: %s\n", bwt_arr);
    printf("Inverse of Burrows - Wheeler Transform: ");
    // Decodes the bwt
    for (i = 0; i < len_bwt; i++) {
        x = l_shift[x];
        printf("%c", bwt_arr[x]);
    }
}
 
// Driver program to test functions above
int main()
{
    char bwt_arr[] = "annb$aa";
    invert(bwt_arr);
    return 0;
}
Producción

Burrows - Wheeler Transform: annb$aa
Inverse of Burrows - Wheeler Transform: banana$

Complejidad de tiempo: O(nLogn) ya que qsort() toma tiempo O(nLogn). 

Ejercicio: Implementar el inverso de Inverse of Burrows – Wheeler Transform en tiempo O(n). 

Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *