stringtranslate.com

direccionamiento abierto

Colisión hash resuelta mediante sondeo lineal (intervalo = 1).

El direccionamiento abierto , o hash cerrado , es un método de resolución de colisiones en tablas hash . Con este método, una colisión hash se resuelve sondeando o buscando en ubicaciones alternativas en la matriz (la secuencia de sonda ) hasta que se encuentra el registro objetivo o se encuentra una ranura de matriz no utilizada, lo que indica que no existe dicha clave en el mesa. [1] Las secuencias de sondas bien conocidas incluyen:

Sondeo lineal
en el que el intervalo entre sondas es fijo, a menudo establecido en 1.
Sondeo cuadrático
en el que el intervalo entre sondas aumenta cuadráticamente (por lo tanto, los índices se describen mediante una función cuadrática).
Doble hash
en el que el intervalo entre sondas es fijo para cada registro pero se calcula mediante otra función hash.

Las principales ventajas y desventajas entre estos métodos son que el sondeo lineal tiene el mejor rendimiento de caché pero es más sensible a la agrupación, mientras que el hash doble tiene un rendimiento de caché deficiente pero prácticamente no muestra agrupación; El sondeo cuadrático se encuentra en el medio en ambas áreas. El doble hash también puede requerir más cálculo que otras formas de sondeo.

Algunos métodos de direccionamiento abiertos, como el hash Rayuela , el hashing Robin Hood , el hash por orden de llegada y el hash cuco mueven las claves existentes en la matriz para dejar espacio para la nueva clave. Esto proporciona tiempos máximos de búsqueda mejores que los métodos basados ​​en sondeo. [2] [3] [4] [5] [6]

Una influencia crítica en el rendimiento de una tabla hash de direccionamiento abierta es el factor de carga ; es decir, la proporción de ranuras de la matriz que se utilizan. A medida que el factor de carga aumenta hacia el 100%, el número de sondas que pueden ser necesarias para encontrar o insertar una clave determinada aumenta drásticamente. Una vez que la tabla se llena, los algoritmos de sondeo pueden incluso no terminar. Incluso con buenas funciones hash, los factores de carga normalmente se limitan al 80%. Una función hash deficiente puede presentar un rendimiento deficiente incluso con factores de carga muy bajos al generar una agrupación significativa, especialmente con el método de direccionamiento lineal más simple. Generalmente, los factores de carga típicos con la mayoría de los métodos de direccionamiento abiertos son del 50 %, mientras que el encadenamiento separado normalmente puede utilizar hasta el 100 %.

Pseudocódigo de ejemplo

El siguiente pseudocódigo es una implementación de una tabla hash de direccionamiento abierta con sondeo lineal y paso a paso de una sola ranura, un enfoque común que es efectivo si la función hash es buena. Cada una de las funciones de búsqueda , configuración y eliminación utiliza una función interna común find_slot para localizar la ranura de la matriz que contiene o debería contener una clave determinada.

par de registros {clave, valor, bandera ocupada (inicialmente no configurada)} par var slot[0], slot[1], ..., slot[num_slots - 1]
función find_slot(clave) i := hash(clave) módulo num_slots // buscamos hasta que encontremos la clave o encontremos un espacio vacío.  mientras (ranura[i] está ocupada) y (ranura[i].clave ≠ clave) i := (i + 1) módulo num_slots volver yo
búsqueda de función (clave) yo: = buscar_ranura (clave) si el slot[i] está ocupado // la clave está en la tabla  return slot[i].value else  // la clave no está en la tabla  return not found
conjunto de funciones (clave, valor) yo: = buscar_ranura (clave) si la ranura [i] está ocupada // encontramos nuestra clave ranura[i].valor:= valor volver  si la mesa está casi llena reconstruir la tabla más grande (nota 1) yo: = buscar_ranura (clave) marcar la ranura [i] como ocupada ranura[i].clave:= clave ranura[i].valor:= valor
nota 1
Reconstruir la tabla requiere asignar una matriz más grande y usar recursivamente la operación set para insertar todos los elementos de la matriz anterior en la nueva matriz más grande. Es común aumentar el tamaño de la matriz exponencialmente , por ejemplo, duplicando el tamaño de la matriz anterior.
función eliminar (tecla) yo: = buscar_ranura (clave) si la ranura [i] está desocupada, devuelve  // ​​la clave no está en la tabla marcar la ranura [i] como desocupada j := yo bucle  (nota 2) j := (j + 1) módulo num_slots si la ranura [j] está desocupada, salga del bucle k := hash(ranura[j].clave) módulo num_slots // determina si k se encuentra cíclicamente en (i,j]  // i ≤ j: | i..k..j |  // i > j: |.k..j i....| o |.. ..j i..k.|  si i ≤ j si (i < k) y (k ≤ j) continuar el bucle  si no (k ≤ j) o (i < k) continuar el bucle marcar la ranura [i] como ocupada ranura[i].clave:= ranura[j].clave ranura[i].valor:= ranura[j].valor marcar la ranura [j] como desocupada yo := j
nota 2
Para todos los registros de un grupo, no debe haber espacios vacantes entre su posición de hash natural y su posición actual (de lo contrario, las búsquedas finalizarán antes de encontrar el registro). En este punto del pseudocódigo, i es un espacio vacante que podría estar invalidando esta propiedad para registros posteriores en el clúster. j es un registro posterior. k es el hash sin formato donde el registro en j aterrizaría naturalmente en la tabla hash si no hubiera colisiones. Esta prueba pregunta si el registro en j está posicionado de manera inválida con respecto a las propiedades requeridas de un grupo ahora que i está vacante.

Otra técnica de eliminación es simplemente marcar la ranura como eliminada. Sin embargo, esto eventualmente requiere reconstruir la tabla simplemente para eliminar los registros eliminados. Los métodos anteriores proporcionan O (1) actualización y eliminación de registros existentes, con reconstrucción ocasional si la marca máxima del tamaño de la tabla crece.

El método de eliminación O (1) anterior solo es posible en tablas hash probadas linealmente con pasos de una sola ranura. En el caso de que se deban eliminar muchos registros en una sola operación, puede ser más eficiente marcar las ranuras para su eliminación y posterior reconstrucción.

Ver también

Referencias

  1. ^ Tenenbaum, Aaron M.; Langsam, Yedidyah; Augenstein, Moshe J. (1990), Estructuras de datos que utilizan C , Prentice Hall, págs. 456–461, págs. 472, ISBN 0-13-199746-7
  2. ^ Poblete; Viola; Munro. "El análisis de un esquema de hash mediante la transformada diagonal de Poisson". pag. 95 de Jan van Leeuwen (Ed.) "Algoritmos - ESA '94". 1994.
  3. ^ Steve Heller. "Programación eficiente en C/C++: más pequeña, más rápida y mejor" 2014. p. 33.
  4. ^ Patricio V. Poblete, Alfredo Viola. "Robin Hood Hashing realmente tiene un costo de búsqueda promedio constante y una variación en tablas completas". 2016.
  5. ^ Paul E. Black, "Hashing último en llegar, primero en ser servido", en Diccionario de algoritmos y estructuras de datos [en línea], Vreda Pieterse y Paul E. Black, eds. 17 de septiembre de 2015.
  6. ^ Paul E. Black, "Robin Hood hash", en Diccionario de algoritmos y estructuras de datos [en línea], Vreda Pieterse y Paul E. Black, eds. 17 de septiembre de 2015.