Su tarea es resolver el problema de la subsecuencia común más larga para n cadenas de longitud 1000.
Una solución válida al problema LCS para dos o más cadenas S 1 , ... S n es cualquier cadena de T de la longitud máxima de tal manera que los personajes de T aparecen en todos los S i , en el mismo orden que en T .
Tenga en cuenta que T no tiene que ser una subcadena de S i .
Ya hemos resuelto este problema en la menor cantidad de código . Esta vez, el tamaño no importa.
Ejemplo
Las cadenas axbycz
y xaybzc
tienen 8 subsecuencias comunes de longitud 3:
abc abz ayc ayz xbc xbz xyc xyz
Cualquiera de estos sería una solución válida para el problema de LCS.
Detalles
Escriba un programa completo que resuelva el problema de LCS, como se explicó anteriormente, respetando las siguientes reglas:
La entrada constará de dos o más cadenas de longitud 1000, que consta de caracteres ASCII con puntos de código entre 0x30 y 0x3F.
Tienes que leer la entrada de STDIN.
Tiene dos opciones para el formato de entrada:
A cada cadena (incluida la última) le sigue un salto de línea.
Las cadenas se encadenan sin separador y sin avance de línea.
El número de cadenas se pasará como un parámetro de línea de comando a su programa.
Debe escribir la salida, es decir, cualquiera de las soluciones válidas para el LCS, en STDOUT, seguido de un salto de línea.
Su idioma de elección debe tener un compilador / intérprete gratuito (como en cerveza) para mi sistema operativo (Fedora 21).
Si necesita algún indicador de compilación o un intérprete específico, por favor mencionelo en su publicación.
Puntuación
Ejecutaré su código con 2, 3, etc. cadenas hasta que tarde más de 120 segundos en imprimir una solución válida. Esto significa que tiene 120 segundos para cada valor de n .
La mayor cantidad de cadenas para las cuales su código terminó a tiempo es su puntaje.
En el caso de un puntaje empatado de n , la presentación que resolvió el problema para n cadenas en el menor tiempo será declarada ganadora.
Todos los envíos serán programados en mi máquina (Intel Core i7-3770, 16 GiB RAM, sin intercambio).
Los n cuerdas de la (n-1) ésimo prueba se generará llamando rand n
(y extracción de los avances de línea, si se solicita), donde rand
se define como sigue:
rand()
{
head -c$[500*$1] /dev/zero |
openssl enc -aes-128-ctr -K 0 -iv $1 |
xxd -c500 -ps |
tr 'a-f' ':-?'
}
La clave está 0
en el código anterior, pero me reservo el derecho de cambiarlo a un valor no revelado si sospecho que alguien está (parcialmente) codificando la salida.
fuente
public static void main(...)
?Respuestas:
C, n = 3 en ~ 7 segundos
Algoritmo
El algoritmo es una generalización directa de la solución estándar de programación dinámica para
n
secuencias. Para 2 cadenasA
yB
, la recurrencia estándar se ve así:Por 3 cadenas
A
,B
,C
utilizo:El código implementa esta lógica para valores arbitrarios de
n
.Eficiencia
La complejidad de mi código es O (s ^ n), con
s
la longitud de las cadenas. Según lo que encontré, parece que el problema es NP-completo. Entonces, si bien el algoritmo publicado es muy ineficiente para valores más grandes den
, en realidad podría no ser posible hacerlo enormemente mejor. Lo único que vi son algunos enfoques que mejoran la eficiencia de los alfabetos pequeños. Dado que el alfabeto es moderadamente pequeño aquí (16), eso podría conducir a una mejora. Todavía predigo que nadie encontrará una solución legítima que vaya más alto quen = 4
en 2 minutos, yn = 4
ya parece ambiciosa.Reduje el uso de memoria en la implementación inicial, para que pueda manejar
n = 4
con el tiempo suficiente. Pero solo produjo la longitud de la secuencia, no la secuencia misma. Consulte el historial de revisiones de esta publicación para ver ese código.Código
Dado que los bucles sobre matrices n-dimensionales requieren más lógica que los bucles fijos, estoy usando un bucle fijo para la dimensión más baja, y solo uso la lógica de bucle genérico para las dimensiones restantes.
Instrucciones para correr
Correr:
lcs.c
.Compile con opciones de alta optimización. Solía:
En Linux, intentaría:
Ejecutar con 2 a 4 secuencias dadas como argumentos de línea de comando:
Si es necesario, entre comillas simples los argumentos de la línea de comando, ya que el alfabeto utilizado para los ejemplos contiene caracteres especiales de shell.
Resultados
test2.sh
ytest3.sh
son las secuencias de prueba de Dennis. No sé los resultados correctos, pero la salida parece al menos plausible.fuente
N_MAX
como una macro y agregar el indicador del compilador-std=c99
para compilar su código con GCC.Esta respuesta está actualmente rota debido a un error. Arreglando pronto ...
C, 2 cuerdas en ~ 35 segundosEsto es en gran medida un trabajo en progreso (como lo muestra el horrible desorden), ¡pero espero que genere algunas buenas respuestas!
El código:
La función relevante que realiza todos los cálculos de LCS es la función
LCS
. El código anterior cronometrará su propia llamada a esta función.Guardar como
main.c
y compilar con:gcc -Ofast main.c -o FLCS
El código se puede ejecutar con argumentos de línea de comando o mediante stdin. Al usar stdin, espera una serie de cadenas seguidas de las propias cadenas.
O:
En una caja Mac OS X con un Intel Core i7 de 1.7Ghz y el caso de prueba que proporcionó Dennis, obtenemos la siguiente salida para 2 cadenas:
El enfoque es muy similar a mi enfoque del desafío anterior, aquí . Además de la optimización anterior, ahora verificamos un número total de caracteres compartidos entre cadenas en cada recursión y salimos temprano si no hay forma de obtener una subcadena más larga de lo que ya existe.
Por ahora, maneja bien 2 cadenas pero tiende a bloquearse más. ¡Más mejoras y una mejor explicación por venir!
fuente