algoritmo para el problema de paridad de prefijo


8

El problema de paridad de prefijo se puede definir de la siguiente manera. Te dan una cuerdaS de longitud n e inicialmente cada personaje es 0. Luego, desea crear una estructura de datos que pueda admitir actualizaciones como las siguientes.

  1. Para una dada i cambio S[i] a cualquiera 0 o 1
  2. para una dada i encontrar la paridad de S[1]+S[2]+...+S[i].

Desde lo alto de mi cabeza, hay una solución que puede admitir este tipo de consultas en O(logn)tiempo, mientras que solo usa espacio lineal y tiempo de preprocesamiento lineal para construir la estructura de datos. La idea es construir un árbol de búsqueda binario completo en la parte superior de la cadena donde las hojas corresponden a caracteres individuales deSy en cada nodo interno almacenamos la suma de todos los caracteres que son hojas en el subárbol definido por ese nodo. De esta manera, podemos soportar trivialmente ambas actualizaciones enO(logn) hora.

Sin embargo, encontré un documento que demuestra un límite inferior para este problema, indicando que no puede hacerlo mejor que O(lognloglogn)para las actualizaciones, y también encontré el siguiente documento http://link.springer.com/chapter/10.1007%2F3-540-51542-9_5 , y un enlace directo al pdf , que proporciona un algoritmo que logra ese límite, siendo así óptimo

Me gustaría entender este algoritmo, sin embargo, la explicación es como 1 página, y faltan muchos detalles.

Entonces, me preguntaba si hay alguna otra fuente para este problema, porque me resulta muy difícil encontrarla, ¿o es esta la única fuente disponible?

gracias de antemano

Respuestas:


9

Leí rápidamente el artículo que vinculaste. Basado en las ideas dadas en ese documento, aquí hay una estructura de datos simple que obtiene unO(Iniciar sesiónnorteIniciar sesiónIniciar sesiónnorte) tiempo limitado en cada operación.

Usted mencionó en su pregunta que puede usar árboles balanceados y aumentados para acelerar esto. En particular, si tiene un árbol binario y aumenta cada nodo con la paridad de su subárbol izquierdo, puede realizar actualizaciones y búsquedas a tiempoO(Iniciar sesiónnorte)cada. Eso es rápido, pero no lo suficientemente rápido.

Ahora, considere la siguiente generalización de su idea. Supongamos que en lugar de usar un árbol binario, usamos un árbol de múltiples vías con factor de ramificaciónk. Aumentamos cada clave en cada nodo con la paridad de todos los subárboles que lo preceden (esto generaliza la idea de almacenar la paridad del subárbol izquierdo). Ahora, pensemos cómo haríamos una búsqueda o actualización en este árbol. Para hacer una búsqueda, utilizamos una versión ligeramente modificada del algoritmo de búsqueda de árbol binario de antes: camine desde la parte superior del árbol hacia abajo, en cada paso acumulando la paridad del subárbol puramente a la izquierda de cada nodo. La altura del árbol en este caso seráO(Iniciar sesiónknorte) y lo hacemos O(1) trabajo por nodo, por lo que el costo de hacer una búsqueda será O(Iniciar sesiónknorte).

Sin embargo, con esta configuración, el costo de hacer una actualización aumenta. En particular, si cambiamos la paridad de un elemento, debemos subir desde la parte inferior del árbol hasta la parte superior, cambiando la paridad almacenada de cada clave en cada nodo en el camino hacia arriba. Existenk claves por nodo y O(Iniciar sesiónknorte) nodos en el camino hacia arriba desde las hojas, por lo que el costo de realizar una operación como esta será O(kIniciar sesiónknorte)=O(kIniciar sesiónkIniciar sesiónnorte), que es demasiado lento Si de alguna manera pudiéramos eliminar este extrak plazo, entonces estaríamos en el negocio.

La idea que tiene el artículo es la siguiente. Si piensa en nuestro problema inicial, teníamos una variedad de tamañosnortey quería poder calcular paridades de prefijos. Ahora tenemos unk-ary árbol donde, en cada nodo, necesitamos poder resolver el problema de paridad de prefijo en matrices de tamaño kcada uno, ya que cada nodo almacena en caché información sobre las capas debajo de él. En la estructura de datos anterior, resolvimos el problema de paridad de prefijo en cada nodo simplemente almacenando una matriz de paridades de prefijo, lo que significa que si necesitamos realizar una actualización, el costo esO(k). La idea del artículo es que al usar una estructura de datos más inteligente en cada nodo, puede realizar estas actualizaciones de manera significativamente más eficiente.

En particular, el documento hace la siguiente idea. Supongamos quekes "pequeño", para alguna definición de pequeño que elegiremos más adelante. Si desea resolver el problema de paridad de prefijo en una matriz de tamañok, entonces solo hay 2k diferentes posibles conjuntos de bits de longitud k. Además, solo hayk posibles consultas de búsqueda que podría hacer en una matriz de bits k. Como resultado, el número de combinaciones posibles de una matriz y una consulta esk2k. Si elegimoskpara ser lo suficientemente pequeño, podemos hacer que esta cantidad sea tan pequeña que sea factible calcular previamente el resultado de cada posible matriz y cada consulta posible. Si hacemos eso, entonces podemos actualizar nuestra estructura de datos de la siguiente manera. En cada nodo de lak-way, en lugar de que cada clave almacene la paridad de su subárbol izquierdo, en su lugar almacenamos una matriz de kbits, uno para cada clave en el nodo. Cuando queremos encontrar la paridad de todos los nodos a la izquierda delyoth niño, solo hacemos una búsqueda en una tabla indexada por aquellos k bits (tratados como un entero) y el índice yo. Siempre que podamos calcular esta tabla lo suficientemente rápido, esto significa que realizar una consulta de paridad de prefijo todavía llevará tiempoO(Iniciar sesiónknorte), pero ahora las actualizaciones llevan tiempo O(Iniciar sesiónknorte) también porque el costo de una consulta de paridad de prefijo en un nodo dado será O(1).

Los autores del artículo notaron que si eliges k=lgnorte2, entonces el número de consultas posibles que se pueden hacer es lgnorte22lgnorte2=lgnorte2norte=o(norte). Además, el costo de realizar cualquier operación en el árbol resultante seráO(Iniciar sesiónknorte)=O(Iniciar sesiónnorteIniciar sesiónlgnorte2)=O(Iniciar sesiónnorteIniciar sesiónIniciar sesiónnorte). El problema es que ahora debes hacero(norte)precomputación al comienzo de la configuración de la estructura de datos. Los autores dan una forma de amortizar este costo utilizando una estructura de datos diferente para las consultas iniciales hasta que se haya realizado suficiente trabajo para justificar la realización del trabajo necesario para configurar la tabla, aunque podría argumentar que necesita gastarO(norte) en primer lugar, construir el árbol y que esto no afectará el tiempo de ejecución general.

Entonces, en resumen, la idea es la siguiente:

  • En lugar de usar un árbol binario aumentado, use un árbol aumentado k-árbol.
  • Tenga en cuenta que con pequeño k, todo posible kLas listas de bits y las consultas en esas listas se pueden calcular previamente.
  • Use esta estructura de datos precalculada en cada nodo del árbol.
  • Escoger k=lgnorte2 para hacer la altura del árbol y, por lo tanto, el costo por operaciones, O(Iniciar sesiónnorteIniciar sesiónIniciar sesiónnorte).
  • Evite el costo inicial de precomputación mediante el uso de una estructura de datos de reemplazo temporal en cada nodo hasta que la precomputación valga la pena.

Con todo, es una estructura de datos inteligente. Gracias por hacer esta pregunta y vincularla. ¡Aprendí mucho en el proceso!

Como anexo, muchas de las técnicas que se utilizaron en esta estructura de datos son estrategias comunes para acelerar soluciones aparentemente óptimas. La idea de calcular previamente todas las consultas posibles en objetos de un tamaño pequeño a menudo se llama Método de cuatro rusos y se puede ver en otras estructuras de datos como la estructura de datos de Fischer-Heun para consultas de rango mínimo o el algoritmo decremental para la conectividad de árbol. De manera similar, la técnica de usar árboles de múltiples vías balanceados aumentados con un factor de ramificación logarítmica surge en otros contextos, como la estructura de datos determinista original para la conectividad de gráficos dinámicos, donde dicho enfoque se usa para acelerar las consultas de conectividad desdeO(Iniciar sesiónnorte) a O(Iniciar sesiónnorteIniciar sesiónIniciar sesiónnorte).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.