Saltar a contenido

Bitap

Definición

El algoritmo Bitap es un algoritmo de búsqueda aproximada de patrones en texto, que usa operaciones bitwise para representar el estado de coincidencias entre el patrón y partes del texto.

Originalmente fue pensado como un algoritmo de búsqueda exacta pero puede adaptarse a búsqueda aproximada al permitir una cantidad determinada de errores (considerando inserciones, eliminaciones o sustituciones) lo que es el Bitap Aproximado.

Esta implementación de búsqueda aproximada retorna un acierto si la distancia de Levenshtein entre el patron y un substring del texto es menor a un valor K dado.

Funcionamiento

El algoritmo empieza creando una mascara de bits de los caracteres del patrón a buscar.

  • Ejemplo para el patrón "ABCA"
Patron A B C A Bitmask
Char b1 b2 b3 b4 b4b3b2b1
A 0 1 1 0 0110
B 1 0 1 1 1101
C 1 1 0 1 1011

Luego para realizar las operaciones de bits y encontrar coincidencias se tiene k+1 vectores de bits de forma(los bits se inicializan en 1):

  • R[0] = coincidencias con 0 errores
  • R[1] = coincidencias con 1 error
  • ...
  • R[k] = coincidencias con K errores.

Luego se recorre el texto en que se esta buscando el patrón, y por cada carácter recorrido se realizan operaciones de bits sobre entre la mascara de bits del carácter actual y todos los vectores de coincidencias bajo estas reglas de actualización:

  • R[0] = ((R[0] << 1) ) | char_mask( C )
  • Caso sin errores permitidos: se shiftean los bits del arreglo de coincidencias y se realiza operación Or contra la mascara de bits del carácter que se esta evaluando, si el bit correspondiente a la posición del carácter en el patrón es 1 se detiene el ciclo y se reinicia la búsqueda empezando del carácter consecutivo, si el bit se vuelve 0 se continua ejecutando, si al final del ciclo el bit mas significativo es 0 entonces se encontró el patrón.
  • Caso con k errores Equivalente al anterior pero en caso de que dentro de un paso el valor del bit correspondiente no cambie a 0 se sigue ejecutando hasta que la cantidad de errores llegue a K.

Ejemplo simple sin errores: Patrón: "ABCA", Texto: "BABCA".

R[0] Shift << Mask Operación Resultado
B 1111 1110 1101 1110 or 1101 1111 X
A 1111 1110 0110 1110 or 0110 1110
B 1110 1100 1101 1100 or 1101 1101
C 1101 1010 1011 1010 or 1011 1011
A 1011 0110 0110 0110 or 0110 0110

Ultimo bit significativo al final de la ejecucion es igual a 0! se encontró el patron dentro del texto.

Implementación

Código en python

def bitap_search_approximate(text: str, pattern: str, max_errors: int):
  """
    Algoritmo Bitap para búsqueda aproximada (fuzzy search)
    :param text: texto donde buscar
    :param pattern: patrón a buscar
    :param max_errors: número máximo de errores permitidos
    :return: lista de posiciones donde se encontró el patrón (aproximadamente)
    """
  m = len(pattern)
  n = len(text)
  if m == 0:
    return []

    # Crear máscaras de bits para cada carácter del patrón
    masks = {}
    for i, ch in enumerate(pattern):
      if ch not in masks:
        masks[ch] = 0
        masks[ch] |= 1 << i  # activa el bit en la posición i

    # Inicializar los vectores R[d] (d = 0..max_errors)
    # Cada bit representa qué prefijos coinciden con <= d errores
    R = [~1 for _ in range(max_errors + 1)]
    match_mask = 1 << (m - 1)
    results = []

    # Iterar sobre cada carácter del texto
    for i, ch in enumerate(text):
      # obtener máscara del carácter actual
      char_mask = masks.get(ch, 0)

      # R[0] (sin errores)
      old_Rd1 = R[0]
      R[0] = ((R[0] << 1) | 1) & char_mask

      # R[d] (con hasta d errores)
      for d in range(1, max_errors + 1):
        tmp = R[d]
        R[d] = (((R[d] << 1) | 1) & char_mask) | (old_Rd1 << 1) | old_Rd1 | R[d - 1]
        old_Rd1 = tmp

        # Si el bit final está en 0, hay coincidencia
        if (R[max_errors] & match_mask) != 0 and i >= m - 1:
          results.append(i - m + 1)

    return results

Referencias

[1] Wikipedia Bitap algorithm, https://en.wikipedia.org/wiki/Bitap_algorith