javascript - 什么是高频使用最快的 levenshtein 算法

标签 javascript algorithm levenshtein-distance

<分区>

对于客户端搜索工具,我需要找到一个词与数百万个其他词的 Levenshtein 距离。用户应该能够将大约二十个单词的短文本与一本书进行比较。用户可以通过查找书中文本中最具特征的单词的位置来做到这一点。 “查找位置”并不意味着寻找完全匹配,而是与 levenshtein 几乎匹配。我从已经可用的实现开始,但我需要更快的速度。我最终得到了这个:

var rowA = new Uint16Array(1e6);
var rowB = new Uint16Array(1e6);
function levenshtein(s1, s2) {
    var s1_len = s1.length, s2_len = s2.length, i1, i2 = 0, a, b, c, c2, i = 0;
    if (s1_len === 0)
        return s2_len;
    if (s2_len === 0)
        return s1_len;
    while (i < s1_len)
        rowA[i] = ++i;
    while (i2 < s2_len) {
        c2 = s2[i2];
        a = i2;
        ++i2;
        b = i2;
        for (i1 = 0; i1 < s1_len; ++i1) {
            c = a + (s1[i1] !== c2 );
            a = rowA[i1];
            b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
            rowB[i1] = b;
        }
        if (i2 === s2_len)
            return b;
        c2 = s2[i2];
        a = i2;
        ++i2;
        b = i2;
        for (i1 = 0; i1 < s1_len; ++i1) {
            c = a + (s1[i1] !== c2 );
            a = rowB[i1];
            b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
            rowA[i1] = b;
        }
    }
    return b;
}

如您所见,我使用了一些技术,例如将对象放在函数之外以便重新使用它们。我还通过稍微线性化循环来重复自己。可以更快吗?我很好奇你的建议。

更新: 在 Bergi 的提示和更多思考之后,我得出了这个解决方案:

    var row = new Uint16Array(1e6);
    function levenshtein(s1, s2) {
        var s1_len = s1.length, s2_len = s2.length, i2 = 1, a, b = 0, c, c2, i1 = 0;
        if (s1_len === 0)
            return s2_len;
        if (s2_len === 0)
            return s1_len;
        c2 = s2[0];
        if (s1[0] === c2) {
            while (i1 < s1_len) {
                row[i1] = i1++;
            }
            b = s1_len - 1;
        } else {
            row[0] = 1;
            ++b;
            if (s1_len > 1)
                for (i1 = 1; i1 < s1_len; ++i1) {
                    if (s1[i1] === c2) {
                        row[i1] = b;
                        for (++i1; i1 < s1_len; ++i1) {
                            row[i1] = ++b;
                        }
                    } else {
                        row[i1] = ++b;
                    }
                }
        }
        if (s2_len > 1)
            while (i2 < s2_len) {
                c2 = s2[i2];
                c = i2 + (s1[0] !== c2);
                a = row[0];
                ++i2;
                b = i2 < a ? (i2 < c ? i2 + 1 : c) : (a < c ? a + 1 : c);
                row[0] = b;
                if (s1_len > 1) {
                    for (i1 = 1; i1 < s1_len; ++i1) {
                        c = a + (s1[i1] !== c2);
                        a = row[i1];
                        b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
                        row[i1] = b;
                    }
                }
            }
        return b;
    }

这又快了很多。我无法从中榨取更多。我一直在寻找其他想法,并会尝试更多。

最佳答案

由于您要反复与同一个词进行比较,因此可以通过使用部分应用程序并在那里进行缓存来提高性能:

function levenshtein(s1) {
    var row0 = [], row1 = [], s1_len = s1.length;
    if (s1_len === 0)
        return function(s2) {
            return s2.length;
        };
    return function(s2) {
        var s2_len = s2.length, s1_idx, s2_idx = 0, a, b, c, c2, i = 0;
        if (s2_len === 0)
            return s1_len;
        …
        return b;
    };
}

I also repeated myself a bit by linearizing the loop somewhat.

不确定它是否变得更快,但您可以省略其中一个数组 - 您不需要以交替方式读/写它们:

function levenshtein(s1) {
    var s1_len = s1.length, row = new Array(s1_len);
    if (s1_len === 0)
        return function(s2) {
            return s2.length;
        };
    return function(s2) {
        var s2_len = s2.length, s1_idx, s2_idx = 0, a, b, c, c2, i = 0;
        if (s2_len === 0)
            return s1_len;
        while (i < s1_len)
           row[i] = ++i;
        while (s2_idx < s2_len) {
            c2 = s2[s2_idx];
            a = s2_idx;
            ++s2_idx;
            b = s2_idx;
            for (s1_idx = 0; s1_idx < s1_len; ++s1_idx) {
                c = a + (s1[s1_idx] === c2 ? 0 : 1);
                a = row[s1_idx];
                b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
                row[s1_idx] = b;
            }
        }
        return b;
    };
}

如果不将数百万个单词放入专用数据结构(例如前缀特里),我认为无法进行进一步的优化。

关于javascript - 什么是高频使用最快的 levenshtein 算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18441763/

相关文章:

javascript - 简单的 RequireJS、AngularJS、Electron App、依赖项未执行

python - 找到两对总和为相同值的对

algorithm - 网络流 - 模拟水管网络

string - 什么是最适合用于比较电视节目标题的字符串距离算法?

elasticsearch - 在Elasticsearch中使用Levenshtein算法进行模糊字符串匹配

javascript - 不引人注目的验证甚至在我的 javascript 调用提交之前就失败了

javascript - 如何缩短这段代码?

javascript - 在 Highcharts 中绘制 2 条线。 JSON格式错误?

随机生成 TSP 解决方案的算法

javascript - 选择中最接近的匹配