2010-02-08 15 views
24

estoy trabajando con un gran conjunto (5-20 millones) de claves de cadena (longitud media de 10 caracteres) cual necesitará almacenar en una estructura de datos de memoria que soporta la operación siguiente en tiempo constante o casi en tiempo constante:forma eficiente de almacenar toneladas de cuerdas (era: aplicación HAT-Trie en Java)

// Returns true if the input is present in the container, false otherwise 
public boolean contains(String input) 

Hashmap de Java está demostrando ser más que satisfactorio en cuanto a rendimiento se refiere, pero está tomando una gran cantidad de la memoria Estoy buscando una solución que sea eficiente desde el punto de vista de la memoria y aún así soporte un rendimiento que sea decente (comparable o casi tan bueno como el hashing).

No me importan los tiempos de inserción/eliminación. En mi aplicación, realizaré solo inserciones (solo en el momento del inicio) y, posteriormente, solo consultaré la estructura de datos utilizando el método contains durante la vida útil de la aplicación.

He leído que la estructura de datos de HAT-Trie es la más cercana para mis necesidades. Me pregunto si hay una biblioteca que tiene una implementación.

Otras sugerencias con sugerencias para implementaciones de bienvenida.

Gracias.

+2

Supongo que cualquier otra estructura de datos utilizará mucha memoria, si se implementa en Java. – ebo

+1

@ebo No si la implementación subyacente usa matrices chars/char. No es necesario que persista el objeto String de entrada. En general, Tries debe usar menos memoria. – hashable

+0

Pregunta muy interesante. –

Respuesta

12

El trie parece una muy buena idea para sus limitaciones.

A "pensar fuera de la caja" alternativa:

si puede permitirse cierta probabilidad de responder "presente" para una cadena que está ausente

EDIT: si no puede pagar los falsos positivos, use un Bloom filter como lo sugiere WizardOfOdds en los comentarios.

Para k = 1, un filtro Bloom es como una tabla hash sin las teclas: cada "cubo" es simplemente un booleano que indica si había al menos una entrada con el mismo hash. Si el 1% de falsos positivos es aceptable, su tabla hash puede ser tan pequeña como aproximadamente 100 * 20 millones de bits o aproximadamente 200 MiB. Para 1 de cada 1000 falsos positivos, 2GiB.

Usar varias funciones hash en lugar de una puede mejorar la tasa de falsos positivos para la misma cantidad de bits.

+3

@Pascaul Cuoq: No te estoy devolviendo el dinero, pero estás reinventando una rueda aquí, probablemente menos eficiente que la que existe. No sé de dónde obtienes tus números, pero hay una estructura de datos conocida que permite un% de falsos positivos, se llama "Bloom Filter". Un filtro de floración para 200 millones de entradas con un 1% de positivos falsos aceptables tomaría 154 MB. – SyntaxT3rr0r

+0

En realidad, 23MB para 20 millones de entradas como el cartel original especificado. Pero, por supuesto, no nos han dicho que los falsos positivos están bien ... –

+0

@WizardOfOdds Gracias por el puntero. Estaba sugiriendo que de hecho es un filtro de floración ingenuo (k = 1). –

2

Para la eficiencia del espacio, la búsqueda O (log (n)) y el código simple, intente la búsqueda binaria en una matriz de caracteres. 20 millones de claves de longitud promedio 10 hacen 200 millones de caracteres: 400 MB si necesita 2 bytes/char; 200 MB si puede salirse con la suya 1. Además de esto, debe representar de alguna manera los límites entre las teclas de la matriz. Si puede reservar un carácter separador, esa es una forma; de lo contrario, podría usar una matriz paralela de compensaciones int.

La variante más simple utilizaría una matriz de cadenas, a un alto costo de espacio por sobrecarga de objetos. Todavía debería vencer a una tabla hash en eficiencia espacial, aunque no tan impresionante.

+0

@Darius Bacon: los diccionarios completos que utilizan la búsqueda O (log n) pueden almacenarse utilizando menos de 10 bits por String (!!!). De Verdad. Menos de 10 bits, lo he hecho. También existen algoritmos de alta compresión para diccionarios que utilizan 12 bits por palabra que también permiten búsquedas rápidas de sugerencias. Pero la pregunta original explícitamente formulada sobre un O (1) contiene, no un O (log n), por lo que no puedo sugerir tal tipo de estructura de datos de "alta compresión, 10 bits por palabra" como respuesta. – SyntaxT3rr0r

+1

Sí, he señalado tales diccionarios comprimidos en mi respuesta a otra pregunta. No probaría nada tan elegante como mi primera sugerencia aquí: tomaría un trabajo considerable hacerlo tan rápido, si es que se puede hacer, ¿no? Y la pregunta pidió * casi * tiempo constante; si esto está cerca tendrá que ser hasta el cartel original. –

+0

(En realidad, este escenario de una tabla hash que pasaba a límites de memoria para cambiar a búsqueda binaria ya se había desempeñado antes en mi vida laboral. El programador más joven que había tenido este problema tramaba una solución compleja, pero la búsqueda binaria funcionaba bien A propósito, introduje los filtros Bloom en otra parte del mismo proyecto ... es como si fuera una preparación para comentar sobre este problema de stackoverflow.) –

4

Google abre una publicación de blog en HAT tries in Java. Pero no veo cómo esto resolverá su problema directamente: la estructura es un trie superficial sobre los prefijos de las claves, con las hojas siendo tablas que contienen los sufijos de todas las claves con el prefijo dado. Entonces, en total, tienes muchas tablas hash que almacenan todas las claves que están en tu hashtable grande actual (quizás guardando algunos bytes por clave en general debido a los prefijos comunes). De cualquier manera, necesita una tabla hash más eficiente en el uso del espacio que la Java Java predeterminada, o la sobrecarga por objeto le golpeará igual de mal.Entonces, ¿por qué no comenzar con una clase de tabla hash especializada solo para claves de cadena, si toma esta ruta, y preocuparse por la parte solamente si todavía parece valer la pena entonces?

2

Similar a un trie es un árbol de búsqueda ternario, pero un árbol de búsqueda ternario tiene la ventaja de utilizar menos memoria. Puede leer sobre los árboles de búsqueda ternarios here, here y here. También uno de los principales documentos sobre el tema por Jon Bentley y Robert Sedgewick es here. También habla sobre ordenar cadenas rápidamente, así que no te dejes llevar por eso.

+0

"Los árboles terciarios son notablemente más grandes que los mapas hash o la mayoría de los diseños binarios" (http: //abc.se/~re/code/tst/tst_docs/perf_notes.html) – ArtemGr

Cuestiones relacionadas