add by zhj: 在学习Lucene的存储结构时,看到其使用了FST,这篇文章写的不错。

trie,FSA,FST都是用来解决有限状态机的存储,trie是树,它进一步演化为FSA和FST,这两者是图

该文的原标题是“使用自动机来索引1,600,000,000个键”,我改了一下,原标题其实是

说这三类数据结构的使用场景。

有限状态机(FSM, finite state machine)可以用来紧密地存储有序集合和有序键值对,并且可以实现快速搜索。本文中,我会表明怎样用FSM来作为数据结构存储这样的数据。

FSM作为数据结构

FSM是一个状态的集合和状态转移的集合。一个起始状态,0个或多个结束状态。一个FSM在同一时间只有一个状态。

FSM非常常见,并且可以用来描述一系列过程。比如我家猫咪Cauchy一天的日常生活:

里面有一些”asleep”或者”eating”的状态,一些转移”food is served”, “something moved”。这里没有结束状态,如果结束了,那真是太恐怖了!

FSM近似的表达了现实中的情况。Cauchy不可能同时吃饭和睡觉,这跟FSM中同一时刻只有一个状态是一样的。并且,从一个状态转移到另一个状态需要外部环境的一个输出。需要睡觉,可能是因为”吃饱了”, “累了”等等。不管他睡得多死,”听到外面的声音”,它总会醒过来。

有序集合

有序集合里的键按照字典序排序。典型的应用是二叉查找树和B树。无序集合,典型应用就是哈希表。这里,我们先描述一个确定无环有限状态接收器(deterministic acyclic finite state acceptor),即FSA。

一个FSA需要满足以下条件:

确定性的。给定已给输入,最多只能转移到一个状态。

无环的。不能反序遍历。

接收器。FSA可以接收一系列特定的输入。

那么,怎么用这些特性来表示一个集合呢。诀窍在于,key作为FSA的状态转移。这样,给定一个输入key,我们可以知道这个key这个key是否在FSA中。

假设一个集合,只有一个key”jul”。FSA就像下面这样:

这时候如果问FSA,是否包含”jul”。处理顺序如下:

给定j,FSA状态从0变为1.

给定u,FSA状态从1变为2.

给定l,FSA状态从2变为3.

输入结束,这时候判断一下FSA是否处在final状态(图中用双圈表示),表明jul确实在set中。

这时候如果问FSA,是否包含”jun”。处理顺序如下:

给定j,FSA状态从0变为1.

给定u,FSA状态从1变为2.

给定l,FSA不动,处理结束。

FSA不动,因为状态2只接收’l’的转移,但是当前输入为’n’。因此处理结束,也表明集合中不包含”jun”。

这时候如果问FSA,是否包含”ju”。处理顺序如下:

给定j,FSA状态从0变为1.

给定u,FSA状态从1变为2.

判断一下,此时是否处于final状态。

值得注意的是,判断一个key是否存在,受限于key的长度,而不是set的大小。

下面把key”mar”添加到FSA中去,这时候FSA的表现如下:

FSA变得稍微复杂一点,状态0可以有两个转移。如果起始输入mar,它会先转移到1状态。

还有一个需要注意的是,状态3被jul和mar两个key共享。即,状态3可以由l和r转移过来。这种共享的方式,可以用更少的空间保存更多的信息。

如果再加入jun,FSA表现如下:

看到变化了么?只有一点点变化。FSA看起来和之前的几乎没什么区别。唯一变化的地方在状态5多了一个转移。FSA其实没有新增状态节点,因为jun和jul共享了前缀ju

下面展示一个更复杂的FSA,包含三个key,october,november,december。

因为有相同的后缀ber,在FSA中只需要编码一次就行了。两个key有更大的相同的后缀,ember。

在介绍FST之前,我们先看看,如何来遍历FSA中所有的key呢?

为了阐述这个过程,还用一个之前的一个简单的图,有三个key,jul,jun和mar。

遍历方式如下:

初始化状态0

移动到状态4,把j添加到key中

移动到状态5,把u添加到key中

移动到状态3,把l添加到key中,输出jul

返回状态5,把key中的l抛弃掉

移动到状态3,把n添加到key中,输出jun

返回状态5,把key中的n抛弃掉

返回状态4,把key中的u抛弃掉

返回状态0,把key中的j抛弃掉

移动到状态1,把m添加到key中

移动到状态2,把a添加到key中

移动到状态3,把r添加到key中,输出mar

这个算法直接应用一个栈存储访问过的状态,和一个栈存储相应的转移。时间复杂度为O(n),空间复杂度O(k),k是set中最长的键的大小。

有序map

和有序集合类似,只是多了一个输出。有序map常用在二叉查找树和b树,无序map就是hashtbale。这里我们介绍一个deterministic acyclic finite state transducer,确定无环有限状态转移器,FST。

FST满足以下特性:

确定性。

无环。

一个转移器。给定一系列输入,会输出一个值。当且仅当输入会达到FST的final状态。

FST和FSA很像,但是对于一个key,FSA只回答了”yer or no”,FST不仅回答”yes or no”,还好返回和这个key相关的一个值。

在有序集合中,只需要把key保存在转移时。但是在这里,还需返回与key对于的value。

一种方法是,在每次转移的时候添加一些值。当输入序列在状态之间转移的时候,输出序列也在慢慢增加。

还是看一个简单的例子吧。map中只包含一个数据jul,对应的value为7:

这和上面的集合差不多,只是在第一个转移状态j之后多了一个相关联的输出7.另外的转移u和l对应的输出都是0,所以图中就不显示了.

如果要判断,FST中是否存在key”jul”,并且需要对应的返回值,处理过程如下:

初始化value为0

给定输入j,FST从状态0转移到1,value+7

给定输入u,FST从状态1转移到2,value+0

给定输入l,FST从状态2转移到3,value+0

输入结束,状态3为final状态,因此key存在,value为7

下面把k-v,”mar 3”添加到FST中

在起始节点,多了一个新的转移m,对应输出为3.如果我们查询jul,那么应该和上面是一样的处理过程。

继续,当添加一个有相同前缀的key,会发生什么呢?

添加key jun,value 6

在状态5和状态3之间添加了一个转移n。但是还有另外两个变化

0->4转移j输入对应输出从7变成了6.

5->3转移l输入对应输出从0变成了1.

这个变化之后们可以正确查询jun和jul,并且返回正确的值。

这种key的属性确保了,即使共享前缀,对于每一个key,然后只有一个唯一的路径可以贯穿整个machine。因此,每个key也有唯一的value。我们要做的就是怎么把这些输出放在转移中去。

其实不仅可以共享前缀,还可以共享后缀。对于两个key tuesday和thursday,分别对于输出3和5.

这两个key有相同的前缀t,相同的后缀sday,按照图里的方式可以保证输出的正确性。

这里在描述输出的时候,其实有一点局限,如果输出不是整形。确实,在FST里用做输出的类型必须满足以下特性:

加法

减法

取前缀(对于整数来说就是min)

构建

Trie树构建

trie树,前缀树。和FSA的区别在于,FSA可以共享前缀和后缀。对于键mon,tues,thus来说,FSA如下:

而trie树只共享前缀,如下:

构建trie树很直接的,对于一个给定的输入,只需要去看看有没有相同的前缀。直到找出相同的前缀,余下的直接转移到一个final状态就可以了。

FSA构建

FSA和trie的区别在于,共享后缀。因此一个FSA的空间会比trie少很多,但是构建起来却更复杂,因此我们如果按照key的字典序插入的话,会好很多,还是用图片来说明。

对于三个key,mon,tues和thurs。按照字典序,插入顺序mon,thurs和tues。先插入mon:

下面插入thurs:

插入thurs的时候,会导致之前的mon被冻结。当FSA中一部分被冻结的时候,我们知道,它以后再也不会被更改了。因为按照字典序排序的,后面的key肯定都是大于等于thurs的。因此不会和mon有相同前缀的key插入了。蓝色的state代表被冻结住,以后不会被更改但是可以被复用。

虚线的状态表示thurs还没有被真正加入到FSA中去,下面插入tues:

在这一步里,我们可以确定hurs会被冻住。因为将会不会有和它有相同前缀的词插入进来了。因为thurs和mon可以有相同的final state了。

这里状态4仍然是虚线,因为还不能确定t开头的key还有没有了。如果下面插入zon:

看到,这时状态4已经被冻住了,因为不会在有t开头的key出现了,另外thurs和tues有一个共同的后缀s,因此状态7和状态9被合并了。

最后,在结束操作以后,把FSA的最后一部分冻住,一个完整的没有重复的结构如下:

因为mon和zon有相同的后缀,因此它们除了第一个状态转移不一样,剩下的可以重复利用。

FST构建

下面快速说一下FST构建,插入键值对 mon-2,tues-3,thurs-5.

直接上图,插入mon-2

对于第一步,我们也可以这样分配输出的值

其实这样也是可以的,但是在算法上来说,把输出放在靠近初始状态的地方,代码写起来更简单。

插入thurs-5

插入tues-3

在把状态0-4之间的输出从5变为3的之后,需要把4之后所有的输出全部加2,除了新添加的key,这样就可以保持输出的平衡。

下面插入一个tye-99

最后的完全形态

fst 共享后缀_trie、FSA、FST(转)相关推荐

  1. fst 共享后缀_FST源代码解读1——FST是什么

    在之前研究IK的时候,看了他存储词典的格式,接触到了前缀树这种数据结构,他的好处是可以共享前缀,但是并不能共享后缀,而FST就能共享后缀,实现更加高效的存储或者查找.当然FST比前缀树添加了一个要求: ...

  2. fst 共享后缀_关于Lucene的词典FST深入剖析

    核心是在于如何快速的依据 查询词 快速的查找到所有的相关文档,这也是 倒排索引(Inverted Index) 的核心思想.那么如何设计一个快速的(常量,或者1)定位词典的数据结构就显得尤其重要.简单 ...

  3. fst 共享后缀_FST源代码解读2——FST的生成

    本篇博客介绍FST的生成.它使用的是生成器模式,因为FST的生成太复杂了,所以必须使用生成器模式,他的类是:org.apache.lucene.util.fst.Builder.Builder(INP ...

  4. fst 共享后缀_谈谈lucene中的FST

    FST是lucene中用来存储字典,并进行检索的核心数据结构,我记得在lucene3.0版本前都用的是跳跃链表(不清楚的同学以后我会专门写一篇磁盘索引技术来说清楚,网上也有相关资料),实际上采用该数据 ...

  5. fst java_java快速序列化库FST

    FST fast-serialization 是重新实现的 Java 快速对象序列化的开发包.序列化速度更快(2-10倍).体积更小,而且兼容 JDK 原生的序列化.要求 JDK 1.7 支持. Ma ...

  6. java fst 入门 例子_Redis 使用 fst 进行序列化

    添加 maven 依赖 de.ruedigermoeller fst 2.57 实现 RedisSerializer 接口 public class FSTSerializer implements ...

  7. lucene随笔-FST(Finite State Transducer)有限状态传感器

    lucene版本:6.5.1 有限状态传感器,FST(Finite State Transducer)在lucene中扮演着非常重要的一个角色,在4.0后的版本lucene大量使用了这种数据结构,主要 ...

  8. 关于Lucene的词典FST深入剖析

    搜索引擎为什么能查询速度那么快? 核心是在于如何快速的依据查询词快速的查找到所有的相关文档,这也是倒排索引(Inverted Index)的核心思想.那么如何设计一个快速的(常量,或者1)定位词典的数 ...

  9. Elasticsearch中FST与前缀搜索

    FST的基本概念 FST(Finite-State Transducer)是一种有限状态自动机,可以将一组输入符号映射为一组输出符号.FST由一组状态和一组转移组成,状态可以是起始状态.接受状态或既是 ...

  10. Lucene 倒排索fst引原理与实现

    整理下几篇博客 1 Lucene 4.X 倒排索引原理与实现: (3) Term Dictionary和Index文件 (FST详细解析) https://www.cnblogs.com/forfut ...

最新文章

  1. 系统怎么手动打补丁_韩国服务器不稳定怎么办?
  2. SpringMVC整合fastdfs-client-java实现web文件上传下载
  3. VMware 收购 Kubernetes 初创公司 Heptio
  4. JSP第四课:用户注册登录设计(内置对象使用)
  5. swith语句的较安全用法
  6. Cloud Foundry中warden的网络设计实现——iptable规则配置
  7. LIS(最大上升子序列)
  8. s3c6410 开发板Linux系统支持 K9GAG08U0E的方法
  9. 【机器学习】怎样将Embedding融入传统机器学习框架?
  10. Apache RocketMQ部署文档
  11. PHP 多参数方法的重构
  12. kali的软件包安装源配置
  13. sentinel 时间窗口的实现
  14. 使软件可二次开发_RobotStudio二次开发:Smart组件I/O信号声明
  15. 奇怪的问题:为什么手机过一年就变得很慢?
  16. cat和EOF的组合妙用
  17. 教你用MSChart控件绘制正态分布图形
  18. easypoi 表头数据导入_使用easypoi根据表头信息动态导出excel
  19. Xposed反射大师脱壳教程
  20. 日语 敬体 简体 作文 对话

热门文章

  1. 【收藏】2018-2019届互联网大厂公司校招薪资汇总,基本年薪都在20万以上
  2. Ubuntu系统中如何删除一个用户
  3. 到底什么是云计算?云计算能干什么?
  4. VMware 配置局域网内访问
  5. 微信小程序一键连接已知wifi
  6. 各个电脑品牌BIOS快捷启动热键
  7. Julia语法归纳总结
  8. 一款高颜值的词云包让我拍案叫绝
  9. 网络的层级及各层级的作用
  10. Spark API 之subtract(差集)