Php 从输入数组中查找数组中相关结果的最快方法

Php 从输入数组中查找数组中相关结果的最快方法,php,javascript,arrays,algorithm,search,Php,Javascript,Arrays,Algorithm,Search,作为一个主要的前端开发人员,这是我不经常深入研究的计算机科学领域,但以下是我的设想: 我有一个字符串的输入,按空格分割,比如说“pintobeans” 我有一个要搜索的结果数组,其中包含如下结果: [“豆,绿豆”,“豆,平托”,“豆,黄”,“豆,蚕豆”] 查找最“相关”的结果(也就是最匹配的结果)的最快方法是什么(最好是javascript或php),例如,在上面的例子中,我想对返回数组进行排序,以便将“bean”、“pinto”放在顶部,其余的放在下面,任何其他结果都放在这些结果的下面 我的第

作为一个主要的前端开发人员,这是我不经常深入研究的计算机科学领域,但以下是我的设想:

我有一个字符串的输入,按空格分割,比如说
“pintobeans”

我有一个要搜索的结果数组,其中包含如下结果:
[“豆,绿豆”,“豆,平托”,“豆,黄”,“豆,蚕豆”]

查找最“相关”的结果(也就是最匹配的结果)的最快方法是什么(最好是javascript或php),例如,在上面的例子中,我想对返回数组进行排序,以便将
“bean”、“pinto”
放在顶部,其余的放在下面,任何其他结果都放在这些结果的下面

我的第一次尝试是将每个结果项与每个输入项进行匹配,并在每个结果项上递增匹配项,然后按多数匹配项排序到最少匹配项

但是,这种方法需要我对整个结果数组进行多次迭代,我觉得我缺乏CS知识,因此这里没有最好的解决方案

/*编辑:以下是我如何处理这个问题的:*/

基于crazedfred的建议和他提到的博客文章(这非常有用),我编写了一些php,基本上使用trie方法和boyer-moore方法的组合,除了从字符串开始搜索(因为我不想在“superbean”中匹配“bean”)

我之所以选择php进行排名,是因为我使用的是js库,在使用方便函数和库开销的同时获得真正的基准不会产生我想要的可测试结果,而且我不能保证它不会在一个或另一个浏览器中爆炸

以下是测试数据:

搜索字符串:
limabeans

结果数组(来自数据库):
[“豆,肾”,“豆,利马”,“豆,海军”,“豆,平托”,“豆,雪莱”,“豆,snap”,“豆,绿豆”,“豆,蚕豆”,“豆,赤豆”,“豆,烤”,“豆,黑”,“豆,豆”,“豆,黑海龟汤”,“豆,蔓越莓(罗马)”,“豆,法国”,“豆,大北方”,“豆,粉红”,“豆,小白”,“豆,黄”,“豆子,白”,“豆子,辣椒”,“豆子,炖菜豆的液体”,“炖菜豆,小豆和玉米”]

首先,在
explode()
对空格上的字符串进行分解之后,我将搜索字符串和结果数组都放入php变量中

然后,我预编译模式以将结果与以下内容进行比较:

$max = max(array_map('strlen',$input));
$reg = array();
for($m = 0; $m < $max; $m++) {
    $reg[$m] = "";
    for($ia = 0; $ia < count($input); $ia++) {
        $reg[$m]. = $input[$ia][$m];
    }
}
这将输出一个数组,其中包含键上的结果,以及我们在值上得到的字符匹配总数

我这样说的原因是为了避免会破坏数据的键冲突,更重要的是,这样我可以快速执行
asort
,并按顺序获得结果

顺序是相反的,在键上,所以在上面的代码块之后,我运行:

asort($sort);
$sort = array_reverse(array_keys($sort));
这给了我一个正确索引的结果数组,从最相关到最不相关排序。我现在可以把它放到我的自动完成框中

因为速度是这个实验的重点,这是我的结果——显然,它们部分依赖于我的计算机

2个输入字,40个结果:~5ms 2个输入字,(一个单个字符,一个完整字符)126个结果:~9ms

显然,这些结果涉及的变量太多,对你来说意义不大,但作为一个例子,我认为这非常令人印象深刻


如果有人认为上面的例子有问题,或者能想出更好的方法,我很乐意听听。我现在唯一能想到的可能是造成问题的原因,就是如果我搜索术语
lean bimas
,我会得到与
lima bean
相同的结果分数,因为该模式不是有条件的基于之前的匹配。因为我正在寻找的结果和我期望的输入字符串不应该经常发生这种情况,所以我决定暂时保持现状,以避免给这个快速的小脚本增加任何开销。但是,如果我最终觉得我的结果被它扭曲了,我会回到这里发布我是如何对该部分进行排序的。

因为您特别指出它可能有几种语言,所以我将以伪代码形式给出答案,以便您能够适应您选择的语言

由于要将一个数组与另一个数组进行匹配,因此性能将根据您的实现而变化很大,因此请尝试几种方法,并考虑何时/如何/多久使用一次

简单的方法是保持数据不变并运行O(n^2)搜索:

如果您首先对数组进行排序(许多语言都实现了一种排序算法,如squicksort,请查看文档!),那么实际匹配会更快。使用您的语言所具有的任何字符串比较:

Sort array X
Sort array Y

Let A = first element of X
Let B = first element of Y

while (A and B are in array)
    if (A > B)
        Next B
    else if (A < B)
        Next A
    else  //match!
        Add A to results
        Next A
        Next B

//handle case where one array is larger (trivial loop)

return results
排序数组X
排序数组Y
设A=X的第一个元素
设B=Y的第一个元素
while(A和B在数组中)
如果(A>B)
下一个B
否则如果(A
现在,上述解决方案的重要部分是,与普通的O(n^2)排序相比,数组排序是否节省了时间。通常,在数组中移动元素很快,而字符串比较不快,因此可能值得。再次尝试这两种方法


最后,有一个Mailinator的家伙想出了一个疯狂的算法,用一些很棒的数据结构在恒定时间内进行巨大的字符串比较。我自己从来没有尝试过,但它一定能工作,因为他在非常低端的硬件上运行他的整个网站。如果你感兴趣,他会在博客上写这个。(注意:这篇博客文章是关于过滤垃圾邮件的,因此文章中的一些词可能有点不自然。)

非常原始

我只是想把这一点放在前面。但这里有一个基于所用术语排序的简单实现。我会在前面
for (every element in array X)
    for (every element in array Y)
        if (current X == current Y)
            add current X to results

return results
Sort array X
Sort array Y

Let A = first element of X
Let B = first element of Y

while (A and B are in array)
    if (A > B)
        Next B
    else if (A < B)
        Next A
    else  //match!
        Add A to results
        Next A
        Next B

//handle case where one array is larger (trivial loop)

return results
var search = "seasoned pinto beans";
var results = ["beans, mung","beans, pinto","beans, yellow","beans, pinto, seasonal","beans, fava"];

// scoreWord
// returns a numeric value representing how much of a match this is to the search term.
// the higher the number, the more definite a match.
function scoreWord(result){
    var terms = search.toLowerCase().split(/\W/),
        words = result.toLowerCase().split(/\W/),
        score = 0;
    // go through each word found in the result
    $.each(words,function(w,word){
        // and then through each word found in the search term
        $.each(terms,function(t,term){
            // exact word matches score higher (a whole point)
            if (term==word)
                score++;
            // a word found in a word should be considered a partial
            // match and carry less weight (1/2 point in this case)
            else if (term.indexOf(word)!=-1||word.indexOf(term)!=-1)
                score+=0.5;
        });
    });
    // return the score
    return score;
}
// go through and sort the array.
results.sort(function(a,b){
    // grab each result's "score", them compare them to see who rates higher in
    // the array
    var aScore = scoreWord(a), bScore = scoreWord(b);
    return (aScore>bScore?-1:(aScore<bScore?1:0));
});
// they are officially "sorted" by relevance at this point.
var query = 'pinto beans';
var results = [ 'beans, mung','beans, pinto','beans, yellow','beans, fava' ];

// Evaluate a regular expression for your 
// query like /pinto|beans/g joining each
// query item with the alternation operator
var pattern = eval( '/' + query.split( ' ' ).join( '|' ) + '/g' );

// Define a function for custom sorting
function regsort( a, b ) {
    var ra = a.match( pattern );
    var rb = b.match( pattern );
    // return the indexing based on 
    // any single query item matched
    return ( rb ? rb.length : 0 ) - ( ra ? ra.length : 0 );
}

// Just launch the sorting
var sorted = results.sort( regsort );

// Should output the right sort:
// ["beans, pinto", "beans, mung", "beans, yellow", "beans, fava"]
console.log( sorted );
var results = ["beans, mung","beans, pinto","beans, yellow","beans, fava"];
var index = {};
for (var i = 0; i < results.length; i ++) {
    results[i].replace(/\w+/g, function(a) {
        if (!index[a]) {
            index[a] = [i];
        } else {
            index[a].push (i);
        }
    });
}
function doSearch(searchString) {
    var words = [];
    var searchResults = [];
    var currentIndex;
    searchString.replace(/\w+/g, function(a) {
        words.push (a);
    });
    for (var i = 0; i < results.length; i ++) {
        searchResults.push ({
            text: results[i],
            score: 0
        });
    }
    for (var i = 0; i < words.length; i ++) {
        if ((currentIndex = index[words[i]])) {
            for (var j = 0; j < currentIndex.length; j ++) {
                searchResults[currentIndex[j]].score ++;
            }
        }
    }
    searchResults.sort (function(a, b) {
        return b.score - a.score;
    });
    return searchResults;
}
[{text:"beans, pinto", score:2}, {text:"beans, mung", score:1}, {text:"beans, yellow", score:1}, {text:"beans, fava", score:1}]