stringtranslate.com

secuencia de Bruijn

La secuencia de De Bruijn para el tamaño del alfabeto k = 2 y la longitud de la subcadena n = 2 . En general, hay muchas secuencias para n y k particulares , pero en este ejemplo es única, hasta el ciclo.

En matemáticas combinatorias , una secuencia de De Bruijn de orden n en un alfabeto A de tamaño k es una secuencia cíclica en la que cada cadena de longitud n posible en A ocurre exactamente una vez como una subcadena (es decir, como una subsecuencia contigua ). Tal secuencia se denota por B ( k , n ) y tiene una longitud k n , que también es el número de cadenas distintas de longitud n en A. Cada una de estas cadenas distintas, cuando se toma como una subcadena de B ( k , n ) , debe comenzar en una posición diferente, porque las subcadenas que comienzan en la misma posición no son distintas. Por lo tanto, B ( k , n ) debe tener al menos k n símbolos. Y dado que B ( k , n ) tiene exactamente k n símbolos, las secuencias de De Bruijn son óptimamente cortas con respecto a la propiedad de contener cada cadena de longitud n al menos una vez.

El número de secuencias distintas de De Bruijn B ( k , n ) es

Las secuencias llevan el nombre del matemático holandés Nicolaas Govert de Bruijn , quien escribió sobre ellas en 1946. [1] Como escribió más tarde, [2] se demostró por primera vez la existencia de secuencias de De Bruijn para cada orden junto con las propiedades anteriores , por El caso de los alfabetos con dos elementos, de Camille Flye Sainte-Marie (1894). La generalización a alfabetos más grandes se debe a Tatyana van Aardenne-Ehrenfest y de Bruijn (1951). Los autómatas para reconocer estas secuencias se denominan autómatas de Bruijn.

En la mayoría de las aplicaciones, A = {0,1}.

Historia

El ejemplo más antiguo conocido de una secuencia de De Bruijn proviene de la prosodia sánscrita donde, desde el trabajo de Pingala , cada posible patrón de tres sílabas de sílabas largas y cortas recibe un nombre, como 'y' para corto-largo-largo y ' m' por mucho-largo-largo. Para recordar estos nombres, se utiliza el mnemónico yamātārājabhānasalagām , en el que cada patrón de tres sílabas ocurre comenzando en su nombre: 'yamātā' tiene un patrón corto-largo-largo, 'mātārā' tiene un patrón largo-largo-largo, y así hasta 'salagām' que tiene un patrón corto-corto-largo. Esta mnemónica, equivalente a una secuencia de De Bruijn sobre 3-tuplas binarias, es de antigüedad desconocida, pero es al menos tan antigua como el libro de Charles Philip Brown de 1869 sobre prosodia sánscrita que la menciona y la considera "una línea antigua, escrita por Pāṇini ". [3]

En 1894, A. de Rivière planteó la cuestión en un número de la revista francesa de problemas L'Intermédiaire des Mathématiciens , de la existencia de una disposición circular de ceros y unos de tamaño que contiene todas las secuencias binarias de longitud . El problema fue resuelto (afirmativamente), junto con el recuento de las distintas soluciones, por Camille Flye Sainte-Marie en el mismo año. [2] Esto se olvidó en gran medida, y Martin (1934) demostró la existencia de tales ciclos para el tamaño general del alfabeto en lugar de 2, con un algoritmo para construirlos. Finalmente, cuando en 1944 Kees Posthumus conjeturó el recuento de secuencias binarias, de Bruijn demostró la conjetura en 1946, gracias a lo cual el problema se hizo bien conocido. [2]

Karl Popper describe de forma independiente estos objetos en su La lógica del descubrimiento científico (1934), llamándolos "secuencias aleatorias más cortas". [4]

Ejemplos

Construcción

Un gráfico de De Bruijn. Cada secuencia de cuatro dígitos ocurre exactamente una vez si uno atraviesa cada borde exactamente una vez y regresa al punto de partida (un ciclo euleriano). Cada secuencia de tres dígitos ocurre exactamente una vez si se visita cada vértice exactamente una vez (un camino hamiltoniano).

Las secuencias de De Bruijn se pueden construir tomando una ruta hamiltoniana de un gráfico de Bruijn de n dimensiones sobre k símbolos (o, de manera equivalente, un ciclo euleriano de un gráfico de Bruijn de dimensiones ( n  − 1). [5]

Una construcción alternativa implica concatenar juntas, en orden lexicográfico, todas las palabras de Lyndon cuya longitud divide a n . [6]

Se puede utilizar una transformada inversa de Burrows-Wheeler para generar las palabras Lyndon requeridas en orden lexicográfico. [7]

Las secuencias de Bruijn también se pueden construir utilizando registros de desplazamiento [8] o mediante campos finitos . [9]

Ejemplo usando el gráfico de De Bruijn

Gráficas dirigidas de dos secuencias B (2,3) de Bruijn y una secuencia B (2,4). En B (2,3), cada vértice se visita una vez, mientras que en B (2,4), cada arista se recorre una vez.

Objetivo: construir una secuencia B (2, 4) de Bruijn de longitud 2 4 = 16 usando  el ciclo del gráfico de Bruijn tridimensional euleriano ( n − 1 = 4 − 1 = 3).

Cada arista en este gráfico tridimensional de De Bruijn corresponde a una secuencia de cuatro dígitos: los tres dígitos que etiquetan el vértice del que sale la arista, seguidos por el que etiqueta la arista. Si uno atraviesa el borde etiquetado como 1 desde 000, llega a 001, indicando así la presencia de la subsecuencia 0001 en la secuencia de De Bruijn. Atravesar cada borde exactamente una vez es utilizar cada una de las 16 secuencias de cuatro dígitos exactamente una vez.

Por ejemplo, supongamos que seguimos el siguiente camino euleriano a través de estos vértices:

000, 000, 001, 011, 111, 111, 110, 101, 011,
110, 100, 001, 010, 101, 010, 100, 000.

Estas son las secuencias de salida de longitud k :

0 0 0 0
_ 0 0 0 1
_ _ 0 0 1 1

Esto corresponde a la siguiente secuencia de De Bruijn:

0 0 0 0 1 1 1 1 0 1 1 0 0 1 0 1

Los ocho vértices aparecen en la secuencia de la siguiente manera:

 {0 0 0 0} 1 1 1 1 0 1 1 0 0 1 0 1 0 {0 0 0 1} 1 1 1 0 1 1 0 0 1 0 1 0 0 {0 0 1 1} 1 1 0 1 1 0 0 1 0 1 0 0 0 {0 1 1 1} 1 0 1 1 0 0 1 0 1 0 0 0 0 {1 1 1 1} 0 1 1 0 0 1 0 1 0 0 0 0 1 {1 1 1 0} 1 1 0 0 1 0 1 0 0 0 0 1 1 {1 1 0 1} 1 0 0 1 0 1 0 0 0 0 1 1 1 {1 0 1 1} 0 0 1 0 1 0 0 0 0 1 1 1 1 {0 1 1 0} 0 1 0 1 0 0 0 0 1 1 1 1 0 {1 1 0 0} 1 0 1 0 0 0 0 1 1 1 1 0 1 {1 0 0 1} 0 1 0 0 0 0 1 1 1 1 0 1 1 {0 0 1 0} 1 0 0 0 0 1 1 1 1 0 1 1 0 {0 1 0 1} 0} 0 0 0 1 1 1 1 0 1 1 0 0 {1 0 1 ... ... 0 0} 0 0 1 1 1 1 0 1 1 0 0 1 {0 1 ... ... 0 0 0} 0 1 1 1 1 0 1 1 0 0 1 0 {1 ...

...y luego volvemos al punto de partida. Cada una de las ocho secuencias de 3 dígitos (correspondientes a los ocho vértices) aparece exactamente dos veces, y cada una de las dieciséis secuencias de 4 dígitos (correspondientes a las 16 aristas) aparece exactamente una vez.

Ejemplo de utilización de la transformación inversa de Burrows-Wheeler

Matemáticamente, una transformada inversa de Burrows-Wheeler en una palabra w genera un conjunto múltiple de clases de equivalencia que consisten en cadenas y sus rotaciones. [7] Cada una de estas clases de equivalencia de cadenas contiene una palabra Lyndon como elemento mínimo único, por lo que se puede considerar que la transformada inversa de Burrows-Wheeler genera un conjunto de palabras Lyndon. Se puede demostrar que si realizamos la transformada inversa de Burrows-Wheeler en una palabra w que consta del alfabeto de tamaño k repetido k n −1 veces (de modo que produzca una palabra de la misma longitud que la secuencia de De Bruijn deseada), entonces el resultado será el conjunto de todas las palabras Lyndon cuya longitud divide a n . De ello se deduce que ordenar estas palabras de Lyndon en orden lexicográfico producirá una secuencia de De Bruijn B ( k , n ), y que esta será la primera secuencia de De Bruijn en orden lexicográfico. Se puede utilizar el siguiente método para realizar la transformada inversa de Burrows-Wheeler, utilizando su permutación estándar :

  1. Ordene los caracteres en la cadena w , dando como resultado una nueva cadena w
  2. Coloque la cadena w encima de la cadena w y asigne la posición de cada letra en w a su posición en w mientras conserva el orden. Este proceso define la permutación estándar.
  3. Escriba esta permutación en notación cíclica con la posición más pequeña en cada ciclo primero y los ciclos ordenados en orden creciente.
  4. Para cada ciclo, reemplace cada número con la letra correspondiente de la cadena w en esa posición.
  5. Cada ciclo ahora se ha convertido en una palabra de Lyndon y están ordenados en orden lexicográfico, por lo que al eliminar los paréntesis se obtiene la primera secuencia de De Bruijn.

Por ejemplo, para construir la secuencia B (2,4) de Bruijn más pequeña de longitud 2 4 = 16, repita el alfabeto (ab) 8 veces para obtener w =abababababababab . Ordene los caracteres en w , dando como resultado w =aaaaaaaabbbbbbbb . Coloque w encima de w como se muestra y asigne cada elemento en w al elemento correspondiente en w dibujando una línea. Numere las columnas como se muestra para que podamos leer los ciclos de la permutación:

Comenzando desde la izquierda, los ciclos de notación de permutación estándar son: (1) (2 3 5 9) (4 7 13 10) (6 11) (8 15 14 12) (16) . (Permutación estándar)

Luego, reemplazando cada número por la letra correspondiente en w de esa columna se obtiene: (a)(aaab)(aabb)(ab)(abbb)(b) .

Estas son todas las palabras de Lyndon cuya longitud divide a 4, en orden lexicográfico, por lo que al eliminar los paréntesis se obtiene B (2,4) = aaaabaabbababbbb .

Algoritmo

El siguiente código Python calcula una secuencia de De Bruijn, dados k y n , basándose en un algoritmo de Generación combinatoria de Frank Ruskey . [10]

desde  escribir  import  Iterable ,  Union ,  Any def  de_bruijn ( k :  Union [ Iterable [ str ],  int ],  n :  int )  ->  str : """secuencia de Bruijn para el alfabeto k  y subsecuencias de longitud n.  """ # Dos tipos de entrada alfabética: un número entero expande # a una lista de números enteros como el alfabeto... si es instancia ( k , int ): alfabeto = lista ( mapa ( str , rango ( k ))) else : # Mientras que cualquier tipo de lista se usa tal como es alfabeto = k k = len ( k )                   a  =  [ 0 ]  *  k  *  n  secuencia  =  [] def  db ( t ,  p ):  si  t  >  n :  si  n  %  p  ==  0 :  secuencia . extender ( a [ 1  :  p  +  1 ] )  else :  a [ t ]  =  a [ t  -  p ]  db ( t  +  1 ,  p )  para  j  en el  rango ( a [ t  -  p ]  +  1 ,  k ):  a [ t ]  =  j  db ( t  +  1 ,  t ) db ( 1 ,  1 )  devuelve  "" . unirse ( alfabeto [ i ]  para  i  en  secuencia )imprimir ( de_bruijn ( 2 ,  3 )) imprimir ( de_bruijn ( "abcd" ,  2 ))

que imprime

00010111aabacadbbcbdccdd

Tenga en cuenta que se entiende que estas secuencias "se ajustan" en un ciclo. Por ejemplo, la primera secuencia contiene 110 y 100 de esta manera.

Usos

Los ciclos de Bruijn son de uso general en experimentos de neurociencia y psicología que examinan el efecto del orden de los estímulos en los sistemas neuronales, [11] y pueden diseñarse especialmente para su uso con imágenes de resonancia magnética funcional . [12]

Detección de ángulo

Los símbolos de una secuencia de De Bruijn escrita alrededor de un objeto circular (como la rueda de un robot ) se pueden utilizar para identificar su ángulo examinando los n símbolos consecutivos enfrentados a un punto fijo. Este problema de codificación de ángulos se conoce como "problema del tambor giratorio". [13] Los códigos Gray se pueden utilizar como mecanismos de codificación posicional rotativos similares, un método que se encuentra comúnmente en codificadores rotativos .

Encontrar el bit establecido menos o más significativo en una palabra

Se puede utilizar una secuencia de De Bruijn para encontrar rápidamente el índice del bit establecido menos significativo ("1 más a la derecha") o el bit establecido más significativo ("1 más a la izquierda") en una palabra mediante operaciones bit a bit y multiplicación. [14] El siguiente ejemplo utiliza una secuencia de Bruijn para determinar el índice del conjunto de bits menos significativo (equivalente a contar el número de bits '0' finales) en un entero sin signo de 32 bits:

uint8_t lowerBitIndex ( uint32_t v ) { static const uint8_t BitPositionLookup [ 32 ] = // tabla hash { 0 , 1 , 28 , 2 , 29 , 14 , 24 , 3 , 30 , 22 , 20 , 15 , 25 , 17 , 4 , 8 , 31 , 27 , 13 , 23 , 21 , 19 , 16 , 7 , 26 , 12 , 18 , 6 , 11 , 5 , 10 , 9 }; return BitPositionLookup [(( uint32_t )(( v & - v ) * 0x077CB531U )) >> 27 ]; }                                                    

La lowestBitIndex()función devuelve el índice del bit establecido menos significativo en v , o cero si v no tiene bits establecidos. La constante 0x077CB531U en la expresión es la secuencia B  (2, 5) 0000 0111 0111 1100 1011 0101 0011 0001 (espacios agregados para mayor claridad). La operación (v & -v)pone a cero todos los bits excepto el conjunto de bits menos significativo, lo que da como resultado un nuevo valor que es una potencia de 2. Esta potencia de 2 se multiplica (módulo aritmético 2 32 ) por la secuencia de De Bruijn, produciendo así un producto de 32 bits. en el que la secuencia de bits de los 5 MSB es única para cada potencia de 2. Los 5 MSB se desplazan a las posiciones del LSB para producir un código hash en el rango [0, 31], que luego se utiliza como índice en la tabla hash Búsqueda de posición de bits. El valor de la tabla hash seleccionado es el índice de bits del bit establecido menos significativo en v .

El siguiente ejemplo determina el índice del bit más significativo establecido en un entero sin signo de 32 bits:

uint32_t mantiene el bit más alto ( uint32_t n ) { n |= ( n >> 1 ); norte |= ( norte >> 2 ); norte |= ( norte >> 4 ); norte |= ( norte >> 8 ); norte |= ( norte >> 16 ); devolver norte - ( norte >> 1 ); }                                 uint8_t lowerBitIndex ( uint32_t v ) { static const uint8_t BitPositionLookup [ 32 ] = { // tabla hash 0 , 1 , 16 , 2 , 29 , 17 , 3 , 22 , 30 , 20 , 18 , 11 , 13 , 4 , 7 , 23 , 31 , 15 , 28 , 21 , 19 , 10 , 12 , 6 , 14 , 27 , 9 , 5 , 26 , 8 , 25 , 24 , }; devolver BitPositionLookup [( keepHighestBit ( v ) * 0x06EB14F9U ) >> 27 ]; }                                                

En el ejemplo anterior se utiliza una secuencia de Bruijn alternativa (0x06EB14F9U), con el correspondiente reordenamiento de los valores de la matriz. La elección de esta secuencia de De Bruijn en particular es arbitraria, pero los valores de la tabla hash deben ordenarse para que coincidan con la secuencia de De Bruijn elegida. La keepHighestBit()función pone a cero todos los bits excepto el bit establecido más significativo, lo que da como resultado un valor que es una potencia de 2, que luego se procesa como en el ejemplo anterior.

Ataques de fuerza bruta a las cerraduras

Una posible secuencia B  (10, 4). Las 2530 subcadenas se leen de arriba a abajo y luego de izquierda a derecha, y sus dígitos se concatenan. Para que la cadena aplique fuerza bruta a un candado de combinación, se añaden los últimos tres dígitos entre paréntesis (000). La cadena de 10003 dígitos es, por tanto, "0 0001 0002 0003 0004 0005 0006 0007 0008 0009 0011 ... 79 7988 7989 7998 7999 8 8889 8899 89 8999 9 000" (espacios añadidos para facilitar la lectura). ).

Se puede utilizar una secuencia de De Bruijn para acortar un ataque de fuerza bruta a un código de bloqueo tipo PIN que no tiene una tecla "enter" y acepta los últimos n dígitos ingresados. Por ejemplo, una cerradura de puerta digital con un código de 4 dígitos (cada dígito tiene 10 posibilidades, del 0 al 9) tendría soluciones B  (10, 4), con longitud10 000 . Por lo tanto, sólo como máximo10 000 + 3 =Se necesitan 10 003 pulsaciones (ya que las soluciones son cíclicas) para abrir la cerradura, mientras que probar todos los códigos por separado requeriría 4 ×10 000 =40.000 prensas .

Principio simplificado del lápiz digital Anoto.
La cámara identifica una matriz de puntos de 6×6, cada uno desplazado de la cuadrícula azul (no impreso) en una de 4 direcciones.
Las combinaciones de desplazamientos relativos de una secuencia de De Bruijn de 6 bits entre las columnas y entre las filas dan su posición absoluta en el papel digital.

secuencias f-fold de Bruijn

Una secuencia de Bruijn n-aria f veces es una extensión de la noción de secuencia de Bruijn n -aria, de modo que la secuencia de la longitud contiene todas las subsecuencias posibles de la longitud n exactamente f veces. Por ejemplo, para las secuencias cíclicas 11100010 y 11101000 son secuencias binarias dobles de De Bruijn. El número de secuencias dobles de De Bruijn, para es , los otros números conocidos [16] son , y .

toro de Bruijn

Modelo STL del toro de De Bruijn (16,32;3,3) 2 con 1 como paneles y 0 como agujeros en la malla: con una orientación consistente, cada matriz de 3 × 3 aparece exactamente una vez

Un toro de De Bruijn es una matriz toroidal con la propiedad de que cada matriz k -aria m -por- n ocurre exactamente una vez.

Un patrón de este tipo puede usarse para codificación posicional bidimensional de una manera análoga a la descrita anteriormente para codificación rotativa. La posición se puede determinar examinando la matriz m por n directamente adyacente al sensor y calculando su posición en el toro de De Bruijn.

decodificación de Bruijn

Calcular la posición de una tupla o matriz única en particular en una secuencia o toro de De Bruijn se conoce como problema de decodificación de De Bruijn . Existen algoritmos de decodificación eficientes para secuencias especiales construidas recursivamente [17] y se extienden al caso bidimensional. [18] La decodificación de De Bruijn es de interés, por ejemplo, en los casos en los que se utilizan secuencias grandes o toros para la codificación posicional.

Ver también

Notas

  1. ^ de Bruijn (1946).
  2. ^ abc de Bruijn (1975).
  3. ^ Marrón (1869); Stein (1963); Kak (2000); Knuth (2006); Salón (2008).
  4. ^ Popper (2002).
  5. ^ Klein (2013).
  6. ^ Según Berstel y Perrin (2007), la secuencia generada de esta manera fue descrita por primera vez (con un método de generación diferente) por Martin (1934), y Fredricksen y Maiorana (1978) observaron la conexión entre ella y las palabras de Lyndon.
  7. ^ ab Higgins (2012).
  8. ^ Goresky y Klapper (2012).
  9. ^ Ralston (1982), págs. 136-139.
  10. ^ "Secuencias de De Bruijn". Sabio . Consultado el 7 de marzo de 2023 .
  11. ^ Aguirre, Mattar y Magis-Weinberg (2011).
  12. ^ "generador de ciclo de Bruijn". Archivado desde el original el 26 de enero de 2016 . Consultado el 15 de septiembre de 2015 .
  13. ^ van Lint y Wilson (2001).
  14. ^ Anderson (1997-2009); Busch (2009)
  15. ^ "secuencia de Bruijn (DeBruijn) (K = 10, n = 3)".
  16. ^ Ósipov (2016).
  17. ^ Tuliani (2001).
  18. ^ Hurlbert e Isaac (1993).

Referencias

enlaces externos