从输入数组中查找数组中相关结果的最快方法
作为一个大多数前端开发人员,这是我不经常深入研究的计算机科学领域,但这是我的方案:
我有一个字符串的输入,分隔空格,说
"pinto beans"
我有一系列搜索结果,包含如下结果:
["beans, mung","beans, pinto","beans, yellow","beans, fava"]
可能是最快的方式(最好是在javascript或php中)找到最“相关”的结果,例如大多数匹配,例如,在上面的例子中,我想对返回数组进行排序,以便将"beans, pinto"
放在顶部,其余的都在下面,任何其他结果都会低于这些。
我的第一次尝试是做一些事情,比如将每个结果项与每个输入项匹配,并在每个输入项上增加匹配,然后按大多数匹配排序到最少。
这种方法需要我多次迭代整个结果数组,我觉得我缺乏CS知识让我没有最好的解决方案。
/ *编辑:这是我最终处理问题的方式:* /
基于crazedfred的建议和他提到的博客文章(非常有用),我写了一些基本上使用trie方法和boyer-moore方法的组合的php,除了从字符串的开头搜索(因为我不喜欢) t想要匹配“superbean”中的“bean”。
基于我使用js库的事实选择php进行排名,并且在使用便利函数和库开销的同时获得真正的基准测试不会产生我之后的可测试结果,我不能保证它赢了在一个浏览器或另一个浏览器中爆炸。
这是测试数据:
搜索字符串:lima beans
结果数组(来自db):["Beans, kidney","Beans, lima","Beans, navy","Beans, pinto","Beans, shellie","Beans, snap","Beans, mung","Beans, fava","Beans, adzuki","Beans, baked","Beans, black","Beans, black turtle soup","Beans, cranberry (roman)","Beans, french","Beans, great northern","Beans, pink","Beans, small white","Beans, yellow","Beans, white","Beans, chili","Beans, liquid from stewed kidney beans","Stew, pinto bean and hominy"]
首先,我将搜索字符串和结果数组都放入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];
}
}
这给了我类似的东西:["lb","ie","ma","an","s"]
然后,我基本上取每个结果字符串(在空格上拆分),并将不区分大小写的字符类与相应的字符编号匹配。如果在比较过程中的任何时候我没有得到任何匹配,我会跳过这个词。这意味着如果只有1个结果以“b”或“l”开头,我只会按WORD运行一次比较,这真的很快。基本上我正在采取trie的一部分来编译搜索,以及Boyer-Moore的不断加速。
这是php - 我尝试过while
s,但foreach
es获得了显着更好的结果:
$sort = array();
foreach($results as $result) {
$matches = 0;
$resultStrs = explode(' ', $result);
foreach($resultStrs as $r) {
$strlen = strlen($r);
for($p = 0; $p < $strlen; $p++) {
if($reg[$p])
preg_match('/^['.$reg[$p].']/i',$r[$p],$match);
if($match==true) {
$matches++;
} else {
break 2;
}
}
}
$sort[$result] = $matches;
}
这会输出一个数组,其中包含键上的结果,以及我们在值上总计得到的字符匹配数。
我这样说的原因是为了避免会破坏我的数据的关键冲突,更重要的是,我可以快速asort
并按顺序获得我的结果。
该顺序是反向的,并且在键上,所以在上面的代码块之后,我运行:
asort($sort);
$sort = array_reverse(array_keys($sort));
这给了我一个正确索引的结果数组,排序最多到最不相关。我现在可以将其放入自动完成框中。
因为速度是这个实验的重点,这是我的结果 - 显然,它们部分依赖于我的计算机。
2输入字,40结果:~5ms
2个输入字,(单个字符,一个整体)126个结果:~9ms
显然,这些结果对你来说意味着太多的变数,但作为一个例子,我认为它非常令人印象深刻。
如果有人看到上面的例子有问题,或者可以想到比这更好的方法,我很乐意听到它。我能想到的唯一一件事就是现在可能会引起问题,如果我要搜索术语lean bimas
,我会得到与lima beans
相同的结果分数,因为该模式不是基于先前的匹配的条件。因为我正在寻找的结果和我期望的输入字符串不应该经常发生这种情况,所以我决定暂时保留它,以避免为这个快速的小脚本添加任何开销。但是,如果我最终觉得我的结果被它扭曲了,我会回到这里并发布关于我如何对该部分进行排序的帖子。
没有找到相关结果
已邀请:
4 个回复
先对冈蒲
如果您首先对数组进行排序(在许多语言中为您实现了一个排序算法,请检查您的文档!),然后实际匹配更快。使用您的语言具有的任何字符串比较:
现在,上述解决方案的重要部分是,如果数组的排序节省了时间而不是普通的O(n ^ 2)排序。通常,在数组中移动元素是快速的而字符串比较不是,所以它可能是值得的。再次尝试两者。 最后,有一个疯狂的算法,Mailinator的家伙梦想使用一些很棒的数据结构在恒定的时间内进行大量的字符串比较。从来没有尝试过,但它必须工作,因为他在非常低端的硬件上运行他的整个网站。如果你有兴趣,他会在这里写博客。 (注意:博客文章是关于过滤垃圾邮件,所以帖子中的一些文字可能只是NSFW。)
甲车劲
我不确定这是否是处理您的请求的最快方法,但肯定它可以是避免嵌套循环+字符串比较的好方法。 希望这可以帮助! 再见
辽躺
犯痪桂涛杭
搜索时,您可以将查询拆分为单词。
将搜索结果构建为
数组的副本,但我将其放在对象中,以便它可以同时保存文本和分数。
然后,对于每个搜索词,增加搜索结果中的分数。
最后,按分数对其进行排序。
做`doSearch(“pinto beans”),它返回一个搜索结果数组以及得分。