Un hash rodante (también conocido como hash recursivo o suma de comprobación rodante) es una función hash en la que la entrada se procesa en una ventana que se mueve a través de la entrada.
Algunas funciones hash permiten calcular un hash continuo muy rápidamente: el nuevo valor hash se calcula rápidamente teniendo en cuenta sólo el valor hash anterior, el valor anterior eliminado de la ventana y el nuevo valor agregado a la ventana, de manera similar a la forma en que la función de media móvil se puede calcular mucho más rápidamente que otros filtros de paso bajo; y similar a la forma en que un hash de Zobrist se puede actualizar rápidamente a partir del valor de hash anterior.
Una de las principales aplicaciones es el algoritmo de búsqueda de cadenas Rabin-Karp , que utiliza el hash rodante que se describe a continuación. Otra aplicación popular es el programa rsync , que utiliza una suma de comprobación basada en adler-32 de Mark Adler como hash rodante. El sistema de archivos de red de bajo ancho de banda (LBFS) utiliza una huella digital de Rabin como hash rodante. FastCDC (Fast Content-Defined Chunking) utiliza una huella digital Gear eficiente en computación como hash rodante.
En el mejor de los casos, los valores hash rodantes son independientes por pares [1] o fuertemente universales . No pueden ser independientes en tres sentidos , por ejemplo.
El algoritmo de búsqueda de cadenas Rabin-Karp a menudo se explica mediante una función hash rodante que solo utiliza multiplicaciones y sumas:
donde es una constante y son los caracteres de entrada (pero esta función no es una huella digital de Rabin , ver más abajo).
Para evitar manipular valores enormes, todos los cálculos se realizan en módulo . La elección de y es fundamental para conseguir un buen hash; en particular, el módulo suele ser un número primo. Consulte el generador congruencial lineal para obtener más información.
Quitar y agregar caracteres simplemente implica sumar o restar el primer o último término. Para desplazar todos los caracteres una posición hacia la izquierda es necesario multiplicar la suma completa por . Para desplazar todos los caracteres una posición hacia la derecha es necesario dividir la suma completa entre . Tenga en cuenta que en aritmética de módulo, se puede elegir tener un inverso multiplicativo por el cual se puede multiplicar para obtener el resultado de la división sin realizar realmente una división.
La huella digital de Rabin es otro hash, que también interpreta la entrada como un polinomio, pero sobre el campo de Galois GF(2) . En lugar de ver la entrada como un polinomio de bytes, se ve como un polinomio de bits, y toda la aritmética se realiza en GF(2) (de manera similar a CRC32 ). El hash es el resultado de la división de ese polinomio por un polinomio irreducible sobre GF(2). Es posible actualizar una huella digital de Rabin utilizando solo el byte de entrada y de salida, lo que la convierte efectivamente en un hash rodante.
Debido a que comparte el mismo autor que el algoritmo de búsqueda de cadenas Rabin-Karp, que a menudo se explica con otro hash rodante más simple, y debido a que este hash rodante más simple también es un polinomio, ambos hashes rodantes a menudo se confunden entre sí. El software de respaldo restic utiliza una huella digital de Rabin para dividir archivos, con un tamaño de blob que varía entre 512 KiB y 8 MiB . [2]
El hash mediante polinomio cíclico [3] , a veces llamado Buzhash, también es simple, pero tiene la ventaja de evitar multiplicaciones y, en su lugar, utiliza desplazamientos de barril . Es una forma de hash de tabulación : supone que hay alguna función hash desde caracteres hasta números enteros en el intervalo . Esta función hash podría ser simplemente una matriz o una tabla hash que asigna caracteres a números enteros aleatorios. Sea la función una rotación binaria cíclica (o desplazamiento circular ): gira los bits 1 hacia la izquierda, empujando el último bit a la primera posición. P.ej, . Sea el exclusivo bit a bit o . Los valores hash se definen como
donde las multiplicaciones por potencias de dos se pueden implementar mediante desplazamientos binarios. El resultado es un número en .
El cálculo de los valores hash de forma continua se realiza de la siguiente manera. Sea el valor hash anterior. Girar una vez: . Si es el personaje que se va a eliminar, gírelo varias veces: . Luego simplemente establezca
¿ Dónde está el nuevo personaje?
El hash mediante polinomios cíclicos es fuertemente universal o independiente por pares: simplemente mantenga los primeros bits. Es decir, tomar el resultado y descartar los bits consecutivos. [1] En la práctica, esto se puede lograr mediante una división de números enteros .
Uno de los casos de uso interesantes de la función hash rodante es que puede crear fragmentos dinámicos basados en contenido de una secuencia o archivo. Esto es especialmente útil cuando se requiere enviar solo los fragmentos modificados de un archivo grande a través de una red: una simple adición de un byte al frente del archivo normalmente haría que todas las ventanas de tamaño fijo se actualizaran, mientras que en realidad, solo la primera "trozo" ha sido modificado. [4]
Un enfoque simple para crear fragmentos dinámicos es calcular un hash rodante, y si el valor hash coincide con un patrón arbitrario (por ejemplo, todos ceros) en los N bits inferiores (con una probabilidad de , dado que el hash tiene una distribución de probabilidad uniforme), entonces es elegido para ser un límite de fragmento. Por tanto, cada fragmento tendrá un tamaño medio de bytes. Este enfoque garantiza que los datos no modificados (a más de un tamaño de ventana de distancia de los cambios) tendrán los mismos límites.
Una vez que se conocen los límites, los fragmentos deben compararse mediante el valor hash criptográfico para detectar cambios. [5] El software de copia de seguridad Borg utiliza el algoritmo Buzhash con un rango de tamaño de fragmento personalizable para dividir secuencias de archivos. [6]
Esta fragmentación definida por contenido se utiliza a menudo para la deduplicación de datos . [4] [6]
Varios programas, incluidos gzip (con la --rsyncable
opción) y rsyncrypto, realizan divisiones basadas en contenido en función de esta suma móvil específica (no ponderada): [7]
dónde
Cambiar la ventana un byte simplemente implica agregar el nuevo carácter a la suma y restar el carácter más antiguo (que ya no está en la ventana) de la suma.
Para todos los lugares , estos programas cortan el archivo entre y . Este enfoque garantizará que cualquier cambio en el archivo solo afecte a su fragmento actual y posiblemente al siguiente, pero no a ningún otro fragmento.
Chunking es una técnica para dividir un flujo de datos en un conjunto de bloques, también llamados fragmentos. La fragmentación definida por contenido (CDC) es una técnica de fragmentación en la que la división del flujo de datos no se basa en un tamaño de fragmento fijo, como en la fragmentación de tamaño fijo, sino en su contenido.
El algoritmo de fragmentación definido por contenido necesita calcular el valor hash de un flujo de datos byte a byte y dividir el flujo de datos en fragmentos cuando el valor hash cumple con un valor predefinido. Sin embargo, comparar una cadena byte por byte introducirá una gran sobrecarga de cálculo. FastCDC [8] propone un enfoque nuevo y eficiente de fragmentación definida por contenido. Utiliza un algoritmo hash Gear de avance rápido, [9] omitiendo la longitud mínima, normalizando la distribución del tamaño de los fragmentos y, por último, pero no menos importante, lanzando dos bytes cada vez para acelerar el algoritmo CDC, que puede lograr un rendimiento aproximadamente 10 veces mayor. que el enfoque de los CDC basado en Rabin. [10]
El pseudocódigo de la versión básica se proporciona de la siguiente manera:
algoritmo de entrada FastCDC : búfer de datos src , longitud de datos n , salida: punto de corte i MinSize ← 2 KB // el tamaño mínimo del fragmento dividido es 2 KB MaxSize ← 64 KB // el tamaño máximo del fragmento dividido es 64 KB Máscara ← 0x0000d93003530000LL fp ← 0 i ← 0 // el tamaño del buffer es menor que el tamaño mínimo del fragmento si n ≤ MinSize entonces devuelve n si n ≥ MaxSize entonces n ← MaxSize // Omita los primeros bytes de MinSize e inicie el hash mientras i < MinSize hace fp ← ( fp << 1 ) + Gear [ src [ i ]] i ← i + 1 mientras que i < n haz fp ← ( fp << 1 ) + Gear [ src [ i ]] si !( fp & Mask ) entonces regresa i i ← i + 1 volver yo
Donde Gear array es una matriz hash precalculada. Aquí, FastCDC utiliza el algoritmo de hash Gear que puede calcular los resultados del hash continuo rápidamente y mantener la distribución uniforme de los resultados del hash como Rabin. En comparación con el algoritmo hash tradicional de Rabin, logra una velocidad mucho más rápida. Los experimentos sugieren que puede generar casi la misma distribución de tamaño de fragmentos en un tiempo mucho más corto (aproximadamente 1/10 de los fragmentos basados en rabin [10] ) al segmentar el flujo de datos.
Todas las funciones hash rodantes se pueden calcular en tiempo lineal en el número de caracteres y actualizarse en tiempo constante cuando los caracteres se desplazan una posición. En particular, calcular el hash rodante de Rabin-Karp de una cadena de longitud requiere operaciones aritméticas modulares, y el hash mediante polinomios cíclicos requiere or exclusivos bit a bit y desplazamientos circulares . [1]
{{cite book}}
: |website=
ignorado ( ayuda )