La distancia de edición (o Levenshtein) entre dos cadenas es el número mínimo de inserciones, eliminaciones y sustituciones de un solo carácter necesarias para transformar una cadena en la otra. Si las dos cadenas tienen una longitud n cada una, es bien sabido que esto puede hacerse en tiempo O (n ^ 2) mediante programación dinámica. El siguiente código de Python realiza este cálculo para dos cadenas s1
y s2
.
def edit_distance(s1, s2):
l1 = len(s1)
l2 = len(s2)
matrix = [range(l1 + 1)] * (l2 + 1)
for zz in range(l2 + 1):
matrix[zz] = range(zz,zz + l1 + 1)
for zz in range(0,l2):
for sz in range(0,l1):
if s1[sz] == s2[zz]:
matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz])
else:
matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz] + 1)
return matrix[l2][l1]
En esta tarea, debe acercarse lo más posible a la distancia de edición pero con una restricción de memoria severa. Su código puede definir una matriz que contiene 1000 enteros de 32 bits y este será el único almacenamiento temporal que use en su cálculo. Todas las variables y estructuras de datos deben estar contenidas en esta matriz. En particular, no podrá implementar el algoritmo anterior en cuanto a las cadenas de longitud 1000, ya que requeriría que almacene al menos 1,000,000 de números. Cuando su idioma no tiene enteros de 32 bits (por ejemplo, Python), simplemente debe asegurarse de no almacenar nunca un número mayor que 2 ^ 32-1 en la matriz.
Puede leer los datos utilizando cualquier biblioteca estándar de su elección sin preocuparse por las restricciones de memoria en esa parte. Para que la competencia sea justa para la parte principal de su código, solo puede usar operaciones que sean funcionalmente equivalentes a las del lenguaje de programación C y no puede usar ninguna biblioteca externa.
Para ser más claro, la memoria para almacenar los datos de entrada o la utilizada por el intérprete de su idioma, JVM, etc. no cuenta para su límite y no puede escribir nada en el disco. Debe asumir que los datos de entrada son de solo lectura cuando están en la memoria, por lo que no puede reutilizarlos para ganar más espacio de trabajo.
¿Qué tengo que implementar?
Su código debe leer en un archivo en el siguiente formato. Tendrá tres líneas. La primera línea es la verdadera distancia de edición. El segundo es la cadena 1 y el tercero es la cadena 2. Lo probaré con los datos de muestra en https://bpaste.net/show/6905001d52e8 donde las cadenas tienen una longitud de 10,000 pero no deberían estar especializadas para estos datos. Debería generar la distancia de edición más pequeña que pueda encontrar entre las dos cadenas.
También deberá probar que su distancia de edición en realidad proviene de un conjunto válido de ediciones. Su código debe tener un interruptor que lo convierta en un modo que pueda usar más memoria (tanto como desee) y genere las operaciones de edición que le dan su distancia de edición.
Puntuación
Tu puntaje será el (optimal edit distance/divided by the edit distance you find) * 100
. Para comenzar, tenga en cuenta que puede obtener una puntuación simplemente contando el número de desajustes entre las dos cadenas.
Puede usar cualquier idioma que desee que esté disponible gratuitamente y sea fácil de instalar en Linux.
Desempate
En el caso de un desempate, ejecutaré su código en mi máquina Linux y el código más rápido gana.
for(int i=0;i<=5;i++)
Se permitiría porque está almacenando datosi
?{ uint32_t foo[1000]; for (foo[0] = 0; foo[0] < 5; ++foo[0]) printf("%d ", foo[0]); }
Esto es asumiendo que se llamará a su matriz de enteros de 32 bitsfoo
.Respuestas:
C ++, puntaje 92.35
Algoritmo de estimación: el algoritmo encuentra el primer lugar en el que las dos cadenas difieren, y luego intenta todas las permutaciones de operación N posibles (insertar, eliminar, reemplazar, los caracteres que coinciden se omiten sin consumir una operación). Califica cada conjunto de operaciones posible en función de cuánto más lejos coincida con éxito ese conjunto de operaciones con las dos cadenas, además de cuánto hace que las longitudes de las cadenas converjan. Después de determinar el conjunto de N operaciones con la puntuación más alta, se aplica la primera operación del conjunto, se encuentra la siguiente falta de coincidencia y el proceso se repite hasta llegar al final de la cadena.
El programa intenta todos los valores de N del 1 al 10 y selecciona el nivel que dio los mejores resultados. N = 10 es generalmente el mejor ahora que el método de puntuación toma en consideración la longitud de la cadena. Los valores más altos de N probablemente serían aún mejores, pero tomarían exponencialmente más tiempo.
Uso de la memoria: dado que el programa es puramente iterativo, necesita muy poca memoria. Solo se usan 19 variables para rastrear el estado del programa. Estos son definidos por #defines para actuar como variables globales.
Uso: El programa se usa igual que el de feersum: se supone que el primer parámetro es el archivo, y cualquier parámetro adicional indica que las ediciones deben mostrarse. El programa siempre imprime la distancia de edición estimada y la puntuación.
Salida de verificación: La salida de verificación se formateó en tres filas:
La fila superior es la cadena de destino, el medio son las operaciones y la inferior es la cadena que se está editando. Los espacios en la línea de operación indican que los caracteres coinciden. 'R' indica que la cadena de edición tiene su carácter en esa posición reemplazado por el carácter de la cadena de destino. 'I' indica que la cadena de edición tiene el carácter de la cadena de destino insertado en esa posición. 'D' indica que la cadena de edición tiene su carácter en esa posición eliminada. Las cadenas de edición y destino tienen espacios insertados cuando el otro tiene un carácter insertado o eliminado para que se alineen.
fuente
C ++ 75.0
El programa está diseñado para trabajar con cadenas de texto arbitrarias. Pueden tener diferentes longitudes siempre que ninguno supere los 13824 caracteres. Utiliza 1.897 enteros de 16 bits, lo que equivale a 949 enteros de 32 bits. Al principio lo escribía en C, pero luego me di cuenta de que no había función para leer una línea.
El primer argumento de la línea de comandos debe ser un nombre de archivo. Si existe un segundo argumento, se imprime un resumen de las ediciones. La primera línea del archivo se ignora, mientras que la segunda y la tercera son las cadenas.
El algoritmo es una versión doblemente bloqueada del algoritmo habitual. Realiza básicamente el mismo número de operaciones, pero, por supuesto, es mucho menos preciso, ya que si una subsecuencia común se divide sobre el borde de un bloque, se pierden muchos de los ahorros potenciales.
fuente
Python,
100Logré calcular la distancia de edición perfectamente en el límite de memoria asignado. Lamentablemente, esta entrada viola dos reglas del desafío, en carta, si no en espíritu.
Primero, en realidad no he almacenado mis datos en 1000 entradas de 32 bits. Para cadenas de 10000 caracteres, mi programa crea dos matrices de 10000 elementos que solo contendrán +1, 0 o -1. A 1.585 bits por número ternario, sería posible empaquetar esos 20000 trits en 31700 bits, dejando 300 bits como más que suficientes para mis 7 enteros restantes de 16 bits.
En segundo lugar, no he implementado el modo requerido para mostrar ediciones. Alternativamente, he implementado un modo que imprime la matriz de edición completa. Es absolutamente posible calcular la ruta de edición desde esa matriz, pero no tengo tiempo en este momento para implementarla.
entrada de ejemplo:
ejemplo de salida detallada:
fuente