Estoy trabajando en una implementación de búsqueda difusa y, como parte de la implementación, estamos utilizando StringUtils.getLevenshteinDistance de Apache. Por el momento, buscamos un tiempo de respuesta promedio máximo máximo para nuestra búsqueda difusa. Después de varias mejoras y con algunos perfiles, el lugar donde se gasta más tiempo es calculando la distancia de Levenshtein. Tarda aproximadamente 80-90% del tiempo total en cadenas de búsqueda de tres letras o más.Modificación del algoritmo Levenshtein Distance para no calcular todas las distancias
Ahora, sé que hay algunas limitaciones a lo que se puede hacer aquí, pero he leído en anteriores preguntas SO y en el enlace de Wikipedia para LD que si uno está dispuesto a limitar el umbral a una distancia máxima establecida, podría ayudar a frenar el tiempo dedicado al algoritmo, pero no estoy seguro de cómo hacerlo exactamente.
Si sólo nos interesa en la distancia si es menor que un umbral k, entonces basta con calcular una franja diagonal de anchura 2k + 1 en la matriz. De esta forma, el algoritmo se puede ejecutar en O (kl) time, donde l es la longitud de la cadena más corta . [3]
A continuación verá el código LH original de StringUtils. Después de eso es mi modificación. Intento básicamente calcular las distancias de una longitud establecida desde la diagonal i, j (por lo tanto, en mi ejemplo, dos diagonales por encima y por debajo de la diagonal i, j). Sin embargo, esto no puede ser correcto ya que lo hice. Por ejemplo, en la diagonal más alta, siempre va a elegir el valor de celda directamente arriba, que será 0. Si alguien me puede mostrar cómo hacer esto funcional como he descrito, o algún consejo general sobre cómo hacerlo tan , podria ser muy apreciado.
public static int getLevenshteinDistance(String s, String t) {
if (s == null || t == null) {
throw new IllegalArgumentException("Strings must not be null");
}
int n = s.length(); // length of s
int m = t.length(); // length of t
if (n == 0) {
return m;
} else if (m == 0) {
return n;
}
if (n > m) {
// swap the input strings to consume less memory
String tmp = s;
s = t;
t = tmp;
n = m;
m = t.length();
}
int p[] = new int[n+1]; //'previous' cost array, horizontally
int d[] = new int[n+1]; // cost array, horizontally
int _d[]; //placeholder to assist in swapping p and d
// indexes into strings s and t
int i; // iterates through s
int j; // iterates through t
char t_j; // jth character of t
int cost; // cost
for (i = 0; i<=n; i++) {
p[i] = i;
}
for (j = 1; j<=m; j++) {
t_j = t.charAt(j-1);
d[0] = j;
for (i=1; i<=n; i++) {
cost = s.charAt(i-1)==t_j ? 0 : 1;
// minimum of cell to the left+1, to the top+1, diagonally left and up +cost
d[i] = Math.min(Math.min(d[i-1]+1, p[i]+1), p[i-1]+cost);
}
// copy current distance counts to 'previous row' distance counts
_d = p;
p = d;
d = _d;
}
// our last action in the above loop was to switch d and p, so p now
// actually has the most recent cost counts
return p[n];
}
Mis modificaciones (sólo a la de bucles):
for (j = 1; j<=m; j++) {
t_j = t.charAt(j-1);
d[0] = j;
int k = Math.max(j-2, 1);
for (i = k; i <= Math.min(j+2, n); i++) {
cost = s.charAt(i-1)==t_j ? 0 : 1;
// minimum of cell to the left+1, to the top+1, diagonally left and up +cost
d[i] = Math.min(Math.min(d[i-1]+1, p[i]+1), p[i-1]+cost);
}
// copy current distance counts to 'previous row' distance counts
_d = p;
p = d;
d = _d;
}
El pensamiento me acaba de ocurrir que yo pueda comprobar si el valor es cero y luego ignórelo o reemplácelo con un valor arbitrariamente alto. Sin embargo, debería pensar en eso un poco más. – AHungerArtist