2009-05-05 32 views
12

Necesito saber si un ternary tree es mejor que un hash table.Ternary Tree Vs Hash Tabla

Me encontré con esta pregunta en una respuesta a another question I had donde alguien dijo que los árboles ternarios a menudo son más rápidos que las tablas hash. Me resulta difícil de creer, así que decidí investigar un poco sobre eso.

This one website from Princeton parece ser la fuente de la creencia. Eché un vistazo al algoritmo que se describe como O (log n + k) donde n es el número de palabras almacenadas, yk es la longitud de la clave.

Me parece que la única forma en que esto podría ser más rápido es si a menudo busca elementos que aún no están almacenados. Otra cosa que me molesta es que el rastreo no continuo de un trie tiende a provocar que accedas a las páginas que se han intercambiado, pero si este es un efecto importante solo se puede ver a través de los puntos de referencia.

Ahora sé que probablemente haya ventajas y desventajas para ambos, y si es así, quiero saber cuáles son. Los puntos de referencia también son útiles.

+0

Dado el contexto, es casi seguro que desee comparar tablas hash con árboles * puramente funcionales *. Los árboles equilibrados imperativos se pueden expresar en términos de matrices, por lo que son mucho más eficientes que sus contrapartes puramente funcionales (que es todo lo que está disponible en Haskell). –

Respuesta

0

Esto es muy intrigante para mí también. Pero desde el wiki que leí, decía que el árbol ternario es más rápido en la mayoría de los problemas de búsqueda. Esto no es sorprendente, porque a pesar de que la tabla hash tiene una búsqueda O (1), todavía necesita tiempo para realizar el hash. Por lo tanto, no es realmente O (1) sino más bien O (k) donde k no depende de N (número de elementos en la estructura de datos). Esto puede dar la impresión de que Hash Table es más rápido. Sin embargo, si se trata de estructuras grandes, la k se suma rápidamente y llegará un punto en el que la velocidad de búsqueda de las Tablas Hash se vuelve más lenta que el Árbol Ternario.

+0

Pero el problema es que los árboles ternarios también dependen de k, especialmente si el elemento está en el árbol. Muchas veces, utilizo tablas hash donde N (número de elementos) es __mucho__ más grande que k (longitud de la clave). – Unknown

7

Esto es lo que deduzco de la Dr. Dobbs Article accesible desde el enlace de Princeton que diste:

  1. ternarios búsqueda árboles son hasta 10% más rápido que las tablas hash sobre algunos problemas de búsqueda. A veces son más lentos, dependiendo en gran medida de la máquina utilizada.
  2. Las TRT son una estructura de datos personalizada ajustada por dos de las mejores mentes de la informática: Jon Bentley y Robert Sedgewick escribieron goodtextbooks, y han hecho su parte de la programación práctica. Las tablas hash se consideran run-of-the-mill.
  3. Las constantes involucradas son importantes, como dice Hao Wooi Lin.
  4. En general, esto depende del problema que está resolviendo. El tiempo de desarrollo más rápido y el soporte casi omnipresente para tablas hash en muchos lenguajes de programación a menudo son más importantes que una mejora del diez por ciento en el tiempo de ejecución.
1

log (n) crece lentamente, por lo que a menudo puede requerir una gran cantidad de datos antes de que sea más lento que un algoritmo O (1) cuando se tiene en cuenta el factor constante.

+2

Bueno, no es __hathat__ enorme. Si toma O (1) para ser O (k) donde k es la longitud de la clave, entonces si tiene k = 10, solo tomará 1025 elementos para que un árbol binario log (n) sea más lento. Para un árbol de ramificación ternaria, es aproximadamente 60,000, que es grande, pero no lo suficientemente grande para que no suceda. – Unknown

+0

@Desconocido: supones que los factores constantes son iguales pero no lo son. En la práctica, las tablas hash son más rápidas que los árboles, incluso en tamaños mucho más pequeños que eso. Por ejemplo, con F # en .NET 4 aquí, un 'Set' puramente funcional es más rápido que .NET' HashSet' para conjuntos con <3 elementos. –

4

La única manera de responder a esta pregunta es empíricamente. La respuesta depende de los detalles de su implementación, qué tipo de búsquedas hace, qué hardware tiene y qué compilador está utilizando. Puedes copiar el código C de Princeton.Si quieres probar un lenguaje funcional, Standard ML tiene tablas hash (mirar SML/NJ), y he aquí algo de ML para ternarios árboles de búsqueda:

type key = Key.ord_key 
type item = Key.ord_key list 

datatype set = NODE of { key : key, lt : set, eq : set, gt : set } 
      | LEAF 

val empty = LEAF 

fun member (_, LEAF) = false 
    | member (h::t, NODE {key, lt, eq, gt}) = 
     (case Key.compare (h, key) 
     of EQUAL => member(t, eq) 
      | LESS => member(h::t, lt) 
      | GREATER => member(h::t, gt)) 
    | member ([], NODE {key, lt, eq, gt}) = 
     (case Key.compare (Key.sentinel, key) 
     of EQUAL => true 
      | LESS => member([], lt) 
      | GREATER => member([], gt)) 

exception AlreadyPresent 

fun insert(h::t, LEAF) = 
     NODE { key = h, eq = insert(t, LEAF), lt = LEAF, gt = LEAF } 
    | insert([], LEAF) = 
     NODE { key = Key.sentinel, eq = LEAF, lt = LEAF, gt = LEAF } 
    | insert(h::t, NODE {key, lt, eq, gt}) = 
     (case Key.compare (h, key) 
     of EQUAL => NODE {key = key, lt = lt, gt = gt, eq = insert(t, eq)} 
      | LESS => NODE {key = key, lt = insert(h::t, lt), gt = gt, eq = eq} 
      | GREATER => NODE {key = key, lt = lt, gt = insert(h::t, gt), eq = eq}) 
    | insert([], NODE {key, lt, eq, gt}) = 
     (case Key.compare (Key.sentinel, key) 
     of EQUAL => raise AlreadyPresent 
      | LESS => NODE {key = key, lt = insert([], lt), gt = gt, eq = eq} 
      | GREATER => NODE {key = key, lt = lt, gt = insert([], gt), eq = eq}) 

fun add(l, n) = insert(l, n) handle AlreadyPresent => n 
+0

Las tablas hash direccionadas abiertas suelen ser 3-18 veces más rápidas que las direcciones cerradas y son la implementación predeterminada de la tabla hash en .NET como consecuencia. Sin embargo, a mi leal saber y entender, ninguna de las implementaciones actuales de OCaml, Standard ML o Haskell es capaz de expresar esta estructura de datos. –

+1

@Jon: Si se refiere al "direccionamiento abierto", lo que se entiende en wikipedia (cada segmento contiene un valor en lugar de un puntero a una cadena separada), esto es fácil de implementar en cualquiera de los dialectos ML. Lo que es un poco tedioso y no puede expresarse en ML es que probablemente desee que los elementos de la matriz estén * desempacados *, una consideración importante para la eficiencia. Las matrices no compartidas no se pueden expresar en Haskell estándar, pero se pueden expresar usando una extensión GHC. Sin embargo, no soy experto en cómo esta característica interactúa con mutablity (usando la mónada IO). –

+0

La mutabilidad en el contexto de las matrices no compartidas no es tanto el problema como los errores de rendimiento en el recolector de basura de GHC. Recientemente agregaron una solución para un error de larga data, pero escribir un solo elemento sigue siendo O (n) y llenar una tabla hash es, por lo tanto, todavía O (n^2) en Haskell. Las implementaciones de FPL de ayer realmente apestan en esto. Si desea comparar objetivamente tablas hash, debe evitarlas. –

0

Usted puede echar un vistazo a tstdb: http://code.google.com/p/tstdb/

Esta kv-store se basa en el Árbol de búsqueda ternario y es compatible con Memcached. Además, tstdb admite la búsqueda de prefijos y la consulta de rango facilitada por el Árbol de búsqueda de ternario.