Tengo un problema de búsqueda bastante complejo que he logrado reducir a la siguiente descripción. He estado buscando en Google pero no he podido encontrar un algoritmo que parezca encajar perfectamente con mi problema. En particular, la necesidad de omitir enteros arbitrarios. ¿Quizás alguien aquí puede señalarme algo?
Tome una secuencia de enteros A, por ejemplo (1 2 3 4)
Tome varias secuencias de enteros y pruebe si alguno de ellos coincide con A tal que.
- A contiene todos los enteros en la secuencia probada
- El orden de los enteros en la secuencia probada es el mismo en A
- No nos interesan los enteros en A que no están en la secuencia de prueba
- Queremos todas las secuencias de prueba coincidentes, no solo la primera.
Un ejemplo
A = (1 2 3 4)
B = (1 3)
C = (1 3 4)
D = (3 1)
E = (1 2 5)
B coincide con A
C coincide con A
D no coincide con A ya que el orden es diferente
E no coincide con A ya que contiene un número entero que no está en A
Espero que esta explicación sea lo suficientemente clara. Lo mejor que he logrado hacer es construir un árbol de las secuencias de prueba e iterar sobre A. La necesidad de poder omitir enteros conduce a muchas rutas de búsqueda sin éxito.
Gracias
Al leer algunas sugerencias, siento que tengo que aclarar un par de puntos que dejé demasiado vagos.
Se permiten números repetidos, de hecho, esto es bastante importante ya que permite que una sola secuencia de prueba coincida con A en varias formas
A = (1234356), B = (236), las coincidencias pueden ser -23 --- 6 o -2--3-6
Espero que haya una gran cantidad de secuencias de prueba, al menos en miles y la secuencia A tenderá a tener una longitud máxima de quizás 20. Por lo tanto, simplemente tratar de hacer coincidir cada secuencia de prueba una por una iterando se vuelve extremadamente ineficiente.
Lo siento si esto no estaba claro.
fuente
Respuestas:
Hmm, puedo pensar en dos posibles algoritmos: un escaneo lineal a través de la secuencia A , o la construcción de un diccionario con búsqueda constante de los índices en el tiempo.
Si está probando muchas subsecuencias potenciales B contra una sola secuencia más grande A , le sugiero que use la variante con el diccionario.
Escaneo lineal
Descripción
Mantenemos un cursor para la secuencia A . Entonces iterar a través de todos los elementos de la subsecuencia B . Para cada elemento, movemos el cursor hacia adelante en A hasta encontrar un elemento coincidente. Si no se encontró ningún elemento coincidente, entonces B no es una subsecuencia.
Esto siempre se ejecuta en O (seq.size) .
Pseudocódigo
Estilo imperativo:
Estilo funcional:
Implementación de ejemplo (Perl):
Búsqueda de diccionario
Descripción
Mapeamos los ítems de la secuencia A a sus índices. Luego buscamos índices adecuados para cada elemento en B , omitimos los índices que son demasiado pequeños y seleccionamos el índice más pequeño posible como límite inferior. Cuando no se encuentran índices, entonces B no es una subsecuencia.
Se ejecuta en algo como O (subseq.size · k) , donde k describe cuántos números duplicados hay
seq
. Más un O (tamaño seq.) sobrecargaLa ventaja de esta solución es que se puede llegar a una decisión negativa mucho más rápido (hasta el tiempo constante), una vez que haya pagado los gastos generales de la creación de la tabla de búsqueda.
Pseudocódigo:
Estilo imperativo:
Estilo funcional:
Implementación de ejemplo (Perl):
Variante de búsqueda de diccionario: codificación como máquina de estados finitos
Descripción
Podemos reducir aún más la complejidad algorítmica hasta O (tamaño subsecuente) si intercambiamos más memoria. En lugar de asignar elementos a sus índices, creamos un gráfico donde cada nodo representa un elemento en su índice. Los bordes muestran posibles transiciones, por ejemplo, la secuencia
a, b, a
tendría los bordesa@1 → b@2, a@1 → a@3, b@2 → a@3
. Este gráfico es equivalente a una máquina de estados finitos.Durante la búsqueda, mantenemos un cursor que inicialmente es el primer nodo del árbol. Luego caminamos el borde de cada elemento de la lista secundaria B . Si no existe tal borde, entonces B no es una sublista. Si después de todos los elementos el cursor contiene un nodo válido, entonces B es una sublista.
Pseudocódigo
Estilo imperativo:
Estilo funcional:
Implementación de ejemplo (Perl):
fuente
study
funciona y si los algoritmos que aplica podrían tener alguna aplicación práctica aquí?study
había construido previamente tablas de búsqueda de caracteres a posición, no muy diferente de mi segunda solución.Aquí hay un enfoque práctico que evita "el trabajo duro" de implementar su propio algoritmo y también evita "reinventar la rueda": utilice una expresión regular motor de para el problema.
Simplemente coloque todos los números de A en una cadena y todos los números de B en una cadena separados por la expresión regular
(.*)
. Agregue un^
personaje al principio y$
al final. Luego, deje que su motor de expresiones regulares favorito busque todas las coincidencias. Por ejemplo, cuandoA = (1234356), B = (236)
crear un registro exp para B como
^(.*)2(.*)3(.*)6(.*)$
. Ahora ejecuta una búsqueda global de expresiones regulares. Para averiguar en qué posiciones en A coinciden sus subsecuencias, simplemente verifique la longitud de los primeros 3 subcoincidencias.Si su rango de enteros deja de 0 a 9, puede considerar codificarlos primero con letras simples para que esto funcione, o debe adaptar la idea usando un carácter de separación.
Por supuesto, la velocidad de ese enfoque dependerá mucho de la velocidad del motor de registro que esté utilizando, pero hay motores altamente optimizados disponibles, y supongo que será difícil implementar un algoritmo más rápido "listo para usar" .
fuente
Este algoritmo debería ser bastante eficiente si obtener la longitud e iterar la secuencia es eficiente.
sequence
y el más corto ensubsequence
sequence
.sequence
igual al número en la posición actual desubsequence
sequence
uno mássubsequence
al final de lasequence
fuente