2009-05-24 8 views
25

¿Puede alguien arrojar algo de luz sobre cómo los lenguajes populares como Python, Ruby implementa tablas hash internamente para la búsqueda de símbolos? ¿Usan el método clásico "matriz con lista enlazada" o usan un árbol equilibrado?¿Cómo se implementan las tablas hash internamente en los idiomas populares?

Necesito un método simple (menos LOC) y rápido para indexar los símbolos en una DSL escrita en C. Me preguntaba qué otros han encontrado más eficiente y práctico.

+3

Tal vez usted desea preguntar "¿Cómo se implementan los mapas ..." como una tabla hash no es la única manera de implementar un mapa! – Artelius

+0

Buen comentario. Pero el problema es que ya he construido el trabajo en tierra en base a los hashes calculados de los símbolos. Por cierto, ¿qué otras formas se implementan los mapas junto a los hashes, que pensé que todos usaban? – CDR

+1

Los mapas a veces también se construyen a partir de árboles binarios. Generalmente se usa cuando el tipo de clave es inmanejable, o si desea conservar un cierto orden de los datos en el mapa (para que pueda iterar de la A a la Z). – Crashworks

Respuesta

16

La clásica "matriz de cubos de hash" que mencionas se usa en todas las implementaciones que he visto.

Una de las versiones más educativas es la implementación de hash en el lenguaje Tcl, en el archivo tcl/generic/tclHash.c. Más de la mitad de las líneas en el archivo son comentarios que explican en detalle en detalle: asignación, búsqueda, diferentes tipos de tablas hash, estrategias, etc. Nota: el código que implementa el lenguaje Tcl es realmente legible.

+0

Las versiones anteriores del código son aún más legibles debido a las cantidades reducidas si ifdeffery, aunque las versiones posteriores son más útiles de manera crítica (admiten personalización de clave y otras cosas por el estilo). –

4

Los árboles equilibrados eliminan el objetivo de las tablas hash ya que una tabla hash puede proporcionar la búsqueda en tiempo constante (amortizado), mientras que la búsqueda promedio en un árbol balanceado es O (log (n)).

El encadenamiento independiente (matriz con lista vinculada) realmente funciona bastante bien si tiene cubos suficientes, y la implementación de la lista vinculada utiliza un asignador de agrupación en lugar de malloc() ing cada nodo del montón individualmente. He encontrado que es casi tan eficiente como cualquier otra técnica cuando está afinado correctamente, y es muy fácil y rápido de escribir. Intente comenzar con 1/8 tantos cubos como datos de origen.

También puede usar open addressing con sondeo cuadrático o polinomial, as Python does.

+0

derrota logarítmica tiempo constante? –

+0

@tydok - "derrotar el propósito" significa no cumplir con el objetivo que la otra solución cumple, por lo que significa "peor que", no "mejor que". –

+0

gaffe :) - –

1

Lo que quiere decir Crashworks era ....

El propósito de las tablas hash son la constante de tiempo de búsqueda, adición y eliminación. En términos de Algoritmo, la operación para todas las operaciones es O (1) amortizada. Considerando que en caso de que use árbol ... el peor tiempo de operación de caso será O (log n) para un árbol balanceado. N es la cantidad de nodos. pero, ¿realmente hemos implementado hash como Tree?

+0

Gracias por señalar mi inclemencia: he corregido mi respuesta. – Crashworks

+3

Un hash implementado como un árbol es un árbol con una API tipo hash en el frente. –

12

Perl usa una matriz con listas vinculadas para contener colisiones. Tiene una heurística simple para duplicar automáticamente el tamaño de la matriz según sea necesario. También hay código para compartir claves entre hashes para guardar un poco de memoria. Puede leer sobre esto en el Perl Illustrated Guts fechado pero aún relevante bajo "HV". Si eres realmente aventurero, puedes profundizar en hv.c.

El algoritmo de hash solía ser bastante simple, pero es probablemente mucho más complicado ahora con Unicode. Debido a que el algoritmo era predecible hubo un ataque DoS por el cual el atacante generó datos que causarían colisiones hash. Por ejemplo, una gran lista de claves enviadas a un sitio web como datos POST. El programa Perl probablemente lo dividiría y lo convertiría en un hash que luego lo metería todo en un solo cubo. El hash resultante fue O (n) en lugar de O (1). Lanza una gran cantidad de solicitudes POST en un servidor y podrías obstruir la CPU. Como resultado, Perl ahora perturba la función hash con un poco de datos aleatorios.

También es posible que desee mirar how Parrot implements basic hashes que es significativamente menos aterrador que la implementación de Perl 5.

En cuanto a "más eficiente y práctico", utilice la biblioteca hash de otra persona. Por el amor de Dios, no escribas uno para usar en producción. Hay un montón de ellos robustos y eficientes por ahí ya.

2

Si usted puede leer Java, es posible que desee revisar el código fuente de sus diversas implementaciones mapa, en particular, HashMap, TreeMap y ConcurrentSkipListMap. Los últimos dos mantienen las llaves ordenadas.

Java HashMap utiliza la técnica estándar que menciona de encadenamiento en cada posición de la cuchara. Utiliza códigos hash bastante débiles de 32 bits y almacena las claves en la tabla. Los autores de Recetas numéricas también dan un ejemplo (en C) de una tabla hash esencialmente estructurada como Java pero en la que (a) asigna los nodos de las listas de depósitos de una matriz, y (b) usa un hash de 64 bits más fuerte codificar y prescindir de almacenar claves en la tabla.

+0

En Java, 'TreeMap' se implementa en base a *** Red-BlackTree ***,' ConcurrentSkipListMap' se implementa en base a *** SkipList ***. – coderz

6

Lua tablas usan un utterly ingenious implemenation que para claves arbitrarias se comporta como 'matriz de cubos', pero si usa números enteros consecutivos como claves, tiene la misma representación y sobrecarga de espacio como una matriz. En la implementación, cada tabla tiene un hash parte y parte de matriz.

creo que ésta es la manera fresca :-)

Cuestiones relacionadas