Google 搜索的即时自动补全功能到底是如何“工做”的?

本文由 yanglbme 原创,首发于公众号“Doocs开源社区”,禁止未受权转载。git

Google 搜索自动补全功能的强大,相信很多朋友都能感觉到,它帮助咱们更快地“补全”咱们所要输入的搜索关键字。那么,它怎么知道咱们要输入什么内容?它又是如何工做的?在这篇文章里,咱们一块儿来看看。github

使用自动补全

Google 搜索的自动补全功能能够在 Google 搜索应用的大多数位置使用,包括 Google 主页、适用于 IOS 和 Android 的 Google 应用,咱们只须要在 Google 搜索框上开始键入关键字,就能够看到联想词了。web

在上图示例中,咱们能够看到,输入关键字 juej,Google 搜索会联想到“掘金”、“掘金小册”、“绝句”等等,好处就是,咱们无须输入完整的关键字便可轻松完成针对这些 topics 的搜索。算法

谷歌搜索的自动补全功能对于使用移动设备的用户来讲特别有用,用户能够轻松在难以键入的小屏幕上完成搜索。固然,对于移动设备用户和台式机用户而言,这都节省了大量的时间。根据 Google 官方报告,自动补全功能能够减小大约 25% 的打字,累积起来,预计天天能够节省 200 多年的打字时间。是的,天天!apache

注意,本文所提到的“联想词”与“预测”,是同一个意思。数据结构

基于“预测”而非“建议”

Google 官方将自动补全功能称之为“预测”,而不是“建议”,为何呢?实际上是有充分理由的。自动补全功能是为了帮助用户完成他们打算进行的搜索,而不是建议用户要执行什么搜索。框架

那么,Google 是如何肯定这些“预测”的?其实,Google 会根据趋势搜索 trends 给到咱们这些“预测”。简单来讲,哪一个热门、哪一个搜索频率高,就更可能推给咱们。固然,这也与咱们当前所处的位置以及咱们的搜索历史相关。elasticsearch

另外,这些“预测”也会随着咱们键入的关键字的变动而更改。例如,当咱们把键入的关键字从 juej 更改成 juex 时,与“掘金”相关的预测会“消失”,同时,与“觉醒”、“决心”相关联的词会出现。函数

为何咱们看不到某些联想词?

若是咱们在输入某个关键字时看不到联想词,那么代表 Google 的算法可能检测到:性能

  • 这个关键字不是热门字词;
  • 搜索的字词太新了,咱们可能须要等待几天或几周才能看到联想词;
  • 这是一个侮辱性或敏感字词,这个搜索字词违反了 Google 的相关政策。更加详细的状况,能够了解 Google 搜索自动补全政策

为何咱们会看到某些不当的联想词?

Google 拥有专门设计的系统,能够自动捕获不适当的预测结果而不显示出来。然而,Google 天天须要处理数十亿次搜索,这意味着 Google 天天会显示数十亿甚至上百亿条预测。再好的系统,也可能存在缺陷,不正确的预测也可能随时会出现。

咱们做为 Google 搜索的用户,若是认定某条预测违反了相关的搜索自动补全政策,能够进行举报反馈,点击右下角“举报不当的联想查询”并勾选相关选项便可。

如何实现自动补全算法?

目前,Google 官方彷佛并无公开搜索自动补全的算法实现,可是业界在这方面已经有了很多研究。

一个好的自动补全器必须是快速的,而且在用户键入下一个字符后当即更新联想词列表。自动补全器的核心是一个函数,它接受输入的前缀,并搜索以给定前缀开头的词汇或语句列表。一般来讲,只须要返回少许的数目便可。

接下来,咱们先从一个简单且低效的实现开始,并在此基础上逐步构建更高效的方法。

词汇表实现

一个简单粗暴的实现方式是:顺序查找词汇表,依次检查每一个词汇,看它是否以给定的前缀开头。

可是,此方法须要将前缀与每一个词汇进行匹配检查,若词汇量较少,这种方式可能勉强行得通。可是,若是词汇量规模较大,效率就过低了。

一个更好的实现方式是:让词汇按字典顺序排序。借助二分搜索算法,能够快速搜索有序词汇表中的前缀。因为二分搜索的每一步都会将搜索的范围减半,所以,总的搜索时间与词汇表中单词数量的对数成正比,即时间复杂度是 O(log N)。二分搜索的性能很好,但有没有更好的实现呢?固然有,往下看。

前缀树实现

一般来讲,许多词汇都以相同的前缀开头,好比 neednested 都以 ne 开头,seedspeed 都以 s 开头。要是为每一个单词分别存储公共前缀彷佛很浪费。

前缀树是一种利用公共前缀来加速补全速度的数据结构。前缀树在节点树中排列一组单词,单词沿着从根节点到叶子节点的路径存储,树的层次对应于前缀的字母位置。

前缀的补全是顺着前缀定义的路径来查找的。例如,在上图的前缀树中,前缀 ne 对应于从子节点取左边缘 N 和惟一边缘 E 的路径。而后能够经过继续遍历从 E 节点能够达到的全部叶节点来生成补全列表。在图中,ne 的补全能够是两个分支:-ed-sted。若是在数中找不到由前缀定义的路径,则说明词汇表中不包含以该前缀开头的单词。

有限状态自动机(DFA)实现

前缀树能够有效处理公共前缀,可是,对于其余共享词部分,仍会分别存储在每一个分支中。好比,后缀 edingtion 在英文单词中特别常见。在上一个例子中,ed 分别存放在了每个分支上。

有没有一种方法能够更加节省存储空间呢?有的,那就是 DFA。

在上面的例子中,单词 neednestedseedspeed 仅由 9 个节点组成,而上一张图中的前缀树包含了 17 个节点。

能够看出,最小化前缀树 DFA 能够在很大程度上减小数据结构的大小。即便词汇量很大,最小化 DFA 一般也适合在内存中存储,避免昂贵的磁盘访问是实现快速自动补全的关键。

一些扩展

上面介绍了如何利用合理的数据结构实现基本的自动补全功能。这些数据结构能够经过多种方式进行扩展,从而改善用户体验。

一般,知足特定前缀的词汇可能不少,而用户界面上可以显示的却很少,咱们更但愿能显示最常搜索或者最有价值的词汇。这一般能够经过为词汇表中的每一个单词增长一个表明单词值的权重 weight,而且按照权重高低来排序自动补全列表。

  • 对于排序后的词汇表来讲,在词汇表每一个元素上增长 weight 属性并不难;
  • 对于前缀树来讲,将 weight 存储在叶子节点中,也是很简单的一个实现;
  • 对于 DFA 来讲,则较为复杂。由于一个叶子节点能够经过多条路径到达。一种解决方案是将权重关联到路径而不是叶子节点。

目前有很多开源库都提供了这个功能,好比主流的搜索引擎框架 ElasticsearchSolr 等,基于此,咱们能够实现高效而强大的自动补全功能。

欢迎关注个人公众号“Doocs开源社区”,原创技术文章第一时间推送。

相关文章
相关标签/搜索