通过"Levenshtein距离"对数组进行排序,在Javascript中具有最佳性能

alt*_*alt 45 javascript sorting jquery levenshtein-distance

所以我有一个随机的javascript数组...

[@ larry,@ nicholas,@ notch]等

它们都以@符号开头.我想用Levenshtein距离对它们进行排序,以便列表顶部的那些最接近搜索项.目前,我有一些使用jQuery的javascript,.grep()使用javascript .match()方法围绕按键输入的搜索词:

(自首次发布以来编辑的代码)

limitArr = $.grep(imTheCallback, function(n){
    return n.match(searchy.toLowerCase())
});
modArr = limitArr.sort(levenshtein(searchy.toLowerCase(), 50))
if (modArr[0].substr(0, 1) == '@') {
    if (atRes.childred('div').length < 6) {
        modArr.forEach(function(i){
            atRes.append('<div class="oneResult">' + i + '</div>');
        });
    }
} else if (modArr[0].substr(0, 1) == '#') {
    if (tagRes.children('div').length < 6) {
        modArr.forEach(function(i){
            tagRes.append('<div class="oneResult">' + i + '</div>');
        });
    }
}

$('.oneResult:first-child').addClass('active');

$('.oneResult').click(function(){
    window.location.href = 'http://hashtag.ly/' + $(this).html();
});
Run Code Online (Sandbox Code Playgroud)

它还有一些if语句,用于检测数组是否包含主题标签(#)或提及(@).忽略这一点.这imTheCallback是名称数组,无论是主题标签还是提及,然后modArr是排序的数组.然后.atResults,.tagResults元素是每次在数组中追加的元素,这将根据输入的搜索词形成一个名称列表.

有Levenshtein距离算法:

var levenshtein = function(min, split) {
    // Levenshtein Algorithm Revisited - WebReflection
    try {
        split = !("0")[0]
    } catch(i) {
        split = true
    };

    return function(a, b) {
        if (a == b)
            return 0;
        if (!a.length || !b.length)
            return b.length || a.length;
        if (split) {
            a = a.split("");
            b = b.split("")
        };
        var len1 = a.length + 1,
            len2 = b.length + 1,
            I = 0,
            i = 0,
            d = [[0]],
            c, j, J;
        while (++i < len2)
            d[0][i] = i;
        i = 0;
        while (++i < len1) {
            J = j = 0;
            c = a[I];
            d[i] = [i];
            while(++j < len2) {
                d[i][j] = min(d[I][j] + 1, d[i][J] + 1, d[I][J] + (c != b[J]));
                ++J;
            };
            ++I;
        };
        return d[len1 - 1][len2 - 1];
    }
}(Math.min, false);
Run Code Online (Sandbox Code Playgroud)

我如何使用算法(或类似的算法)到我当前的代码中进行排序而没有糟糕的性能?

更新:

所以我现在正在使用James Westgate的Lev Dist功能.快速工作WAYYYY.所以性能得到了解决,现在的问题是将它与源码一起使用......

modArr = limitArr.sort(function(a, b){
    levDist(a, searchy)
    levDist(b, searchy)
});
Run Code Online (Sandbox Code Playgroud)

我现在的问题是对使用该.sort()方法的一般理解.非常感谢帮助,谢谢.

谢谢!

Jam*_*ate 101

几年前我写了一个内联拼写检查程序并实现了Levenshtein算法 - 因为它是内联的,对于IE8我做了很多性能优化.

var levDist = function(s, t) {
    var d = []; //2d matrix

    // Step 1
    var n = s.length;
    var m = t.length;

    if (n == 0) return m;
    if (m == 0) return n;

    //Create an array of arrays in javascript (a descending loop is quicker)
    for (var i = n; i >= 0; i--) d[i] = [];

    // Step 2
    for (var i = n; i >= 0; i--) d[i][0] = i;
    for (var j = m; j >= 0; j--) d[0][j] = j;

    // Step 3
    for (var i = 1; i <= n; i++) {
        var s_i = s.charAt(i - 1);

        // Step 4
        for (var j = 1; j <= m; j++) {

            //Check the jagged ld total so far
            if (i == j && d[i][j] > 4) return n;

            var t_j = t.charAt(j - 1);
            var cost = (s_i == t_j) ? 0 : 1; // Step 5

            //Calculate the minimum
            var mi = d[i - 1][j] + 1;
            var b = d[i][j - 1] + 1;
            var c = d[i - 1][j - 1] + cost;

            if (b < mi) mi = b;
            if (c < mi) mi = c;

            d[i][j] = mi; // Step 6

            //Damerau transposition
            if (i > 1 && j > 1 && s_i == t.charAt(j - 2) && s.charAt(i - 2) == t_j) {
                d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost);
            }
        }
    }

    // Step 7
    return d[n][m];
}
Run Code Online (Sandbox Code Playgroud)

  • 有趣的是,`var`关键字用于实际目的,而不是重置按钮. (5认同)
  • 谢谢!老实说,这是我在网上看到的唯一一个出色的表现功能.所有其他人在长弦上变得非常慢,这太棒了.做得好.拍手* (3认同)
  • 我没有完成代码,但我已经做了快速测试,并且可以验证这比OP的算法快得多.干得好! (2认同)
  • 一件小事:我注意到这是你计算的Damerau-Levenshtein距离,而不仅仅是Levenshtein距离.如果你削减了换位测试,这将更接近OP想要的,并且运行得更快.:) (2认同)
  • 如果设置限制,并取出DT,那么性能就会过时.http://jsperf.com/levenshtein-distance/2注意:我的原始实现有按字母顺序排序的项目桶,然后字母表中的每个字母也有一个长度的桶,这样我可以只比较相同长度的项目或者在没有算法内部检查的情况下长度达到极限. (2认同)

Mar*_*Wit 13

我来到这个解决方案:

var levenshtein = (function() {
        var row2 = [];
        return function(s1, s2) {
            if (s1 === s2) {
                return 0;
            } else {
                var s1_len = s1.length, s2_len = s2.length;
                if (s1_len && s2_len) {
                    var i1 = 0, i2 = 0, a, b, c, c2, row = row2;
                    while (i1 < s1_len)
                        row[i1] = ++i1;
                    while (i2 < s2_len) {
                        c2 = s2.charCodeAt(i2);
                        a = i2;
                        ++i2;
                        b = i2;
                        for (i1 = 0; i1 < s1_len; ++i1) {
                            c = a + (s1.charCodeAt(i1) === c2 ? 0 : 1);
                            a = row[i1];
                            b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
                            row[i1] = b;
                        }
                    }
                    return b;
                } else {
                    return s1_len + s2_len;
                }
            }
        };
})();
Run Code Online (Sandbox Code Playgroud)

另见http://jsperf.com/levenshtein-distance/12

通过消除一些阵列使用获得了大多数速度.


The*_*ion 6

更新:http://jsperf.com/levenshtein-distance/5

新版本摧毁了所有其他基准测试.我特别追逐Chromium/Firefox性能,因为我没有IE8/9/10测试环境,但优化应该适用于大多数浏览器.

Levenshtein距离

执行Levenshtein距离的矩阵可以一次又一次地重复使用.这是一个明显的优化目标(但要小心,这现在对字符串长度施加了限制(除非你动态调整矩阵的大小)).

在jsPerf Revision 5中没有追求优化的唯一选择是memoisation.根据您对Levenshtein Distance的使用情况,这可能会有很大帮助,但由于其特定的实现性而被省略.

// Cache the matrix. Note this implementation is limited to
// strings of 64 char or less. This could be altered to update
// dynamically, or a larger value could be used.
var matrix = [];
for (var i = 0; i < 64; i++) {
    matrix[i] = [i];
    matrix[i].length = 64;
}
for (var i = 0; i < 64; i++) {
    matrix[0][i] = i;
}

// Functional implementation of Levenshtein Distance.
String.levenshteinDistance = function(__this, that, limit) {
    var thisLength = __this.length, thatLength = that.length;

    if (Math.abs(thisLength - thatLength) > (limit || 32)) return limit || 32;
    if (thisLength === 0) return thatLength;
    if (thatLength === 0) return thisLength;

    // Calculate matrix.
    var this_i, that_j, cost, min, t;
    for (i = 1; i <= thisLength; ++i) {
        this_i = __this[i-1];

        for (j = 1; j <= thatLength; ++j) {
            // Check the jagged ld total so far
            if (i === j && matrix[i][j] > 4) return thisLength;

            that_j = that[j-1];
            cost = (this_i === that_j) ? 0 : 1;  // Chars already match, no ++op to count.
            // Calculate the minimum (much faster than Math.min(...)).
            min    = matrix[i - 1][j    ] + 1;                      // Deletion.
            if ((t = matrix[i    ][j - 1] + 1   ) < min) min = t;   // Insertion.
            if ((t = matrix[i - 1][j - 1] + cost) < min) min = t;   // Substitution.

            matrix[i][j] = min; // Update matrix.
        }
    }

    return matrix[thisLength][thatLength];
};
Run Code Online (Sandbox Code Playgroud)

Damerau-Levenshtein距离

jsperf.com/damerau-levenshtein-distance

Damerau-Levenshtein距离是对Levenshtein距离的一个小修改,包括换位.优化很少.

// Damerau transposition.
if (i > 1 && j > 1 && this_i === that[j-2] && this[i-2] === that_j
&& (t = matrix[i-2][j-2]+cost) < matrix[i][j]) matrix[i][j] = t;
Run Code Online (Sandbox Code Playgroud)

排序算法

这个答案的第二部分是选择合适的排序函数.我将很快将优化的排序功能上传到http://jsperf.com/sort.