| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 数据结构与算法 -> 学习笔记——搞懂FST数据结构 -> 正文阅读 |
|
[数据结构与算法]学习笔记——搞懂FST数据结构 |
目录 查看builder类(package org.apache.lucene.util.fst) 查看BlockTreeTermsWriter类(packageorg.apache.lucene.codecs.blocktree) FST存储到文件中的存储结构(TermIndex词项索引和TermDictionary词项字典) 首先声明图片数据来源:b站马士兵教育。 luncence源码百度云: 链接:https://pan.baidu.com/s/12EQtJ_t-P3-P_CJnHIfnEw? 数据结构FST简述了解一下trie前缀树:复用所有前缀
FST简述及它的查询过程FST:有限状态转化机
从Lucence源码中看FST的结构查看builder类(package org.apache.lucene.util.fst)
描述边,也就是出度的类是Arc
查看BlockTreeTermsWriter类(packageorg.apache.lucene.codecs.blocktree)PendingEntry—PendingBlock --PendingTerm
通过构建过程来理解: 2.输入abfi,abfj,abfk,此时可以看到f已经有三个出度k I j了,初步满足聚合成Block的条件 此时还都是term,没有聚合成block,虽然已经满足了条件给出的3个。但是由于输入项还没确定完.所以还不能聚合起来。 3输入abgl---->聚合成Block
FST存储到文件中的存储结构(TermIndex词项索引和TermDictionary词项字典).tip: 存放Term-Index .tim:存放Term-Dictonary Term-index里面存放的FSTindex存放Block的前缀记录,末尾指向block..这些block具体存放在.tim文件也就是term Dictonary里面。 block具体存放的是啥? ? BlockHeader: Suffix: states: metaLength: 再来看看FST-Index和block更详细的内容 ? 以这样一个词项字典 还是这张图 构建FST-Index 其中flag是标志位,代表着一个block的开始. 字符’a’以及’c’代表着以’a’开始的block和’c’开始的block 数字12,36,64等代表着在数组中的起始位置. 逐个看看: ? 这一块对应的是这个a有两个子节点,所以EntryCount为2 ? 这一块对应的是这个 EntryCount为5,代表五个后缀节点 Suffix里面包含了a,b,c,d,e五个,他们所代表的属性分别是PendingTerm, PendingTerm, PendingBlock,FloorBlock, PendingTerm ? 这一块对应的是这个 suffix:是a,b,c三个后缀 ? 这块对应的是这个最多后缀的d节点: 以上是FST-Index以及Block的映射情况。 FST构建(读取和写入数据)上面的那些都是一些概念定义,对上面的知识点有了大概的认识之后,就可以真正搞懂FST这个牛x的数据结构是怎么读写数据的了。 FST的写数据过程最好是结合它的读的过程去理解,不然会感觉很懵逼。 首先,FST里面包含了两个重要的属性: Node和Arc,分别代表结点和出度(边) Node作为节点,其实它在这里主要起到了描述节点状态的作用,描述节点是否已经经过处理。它是一个基类,子类有UncomliedNode和CompliedNode Arc是出度,它里面算是大有文章了。 四个属性: label:存放输入的字符,实际存的是ASCLL码二进制 output:存放输出值,还记得前面说的FST类似于哈希表吗,可以理解为存放k-v中的v值 以上两个属性比较好理解,难点重点是下面两个: target:存放下标值,如果当前出度指向的字符不是输入值的最后一个时,会存储index值来指向下一个字符的下标。事实上target这个属性就是在flags属性里面的BIT_TARGET_NEXT状态不存在时才会出现的。 flags: 这里面用到比特位来存储,主要作用应该是压缩占用空间。利用标志位在读的时候可以解码。想想一个数字可以存放这么多状态, 有点类似于以前学计组时接触过的flag标志位。 flags里面有六个状态,除了BIT_TARGET_NEXT不好理解,其它应该比较简单。这里我也不赘述。 BIT_TARGET_NEXT这个状态一开始我去查百度,大多说的都是TARGET_NEX优化,但是都没有说这个优化是什么。这里我简单理解总结一下就是,当这个状态激活时,我们在读到这个字符的时候不需要发生跳转,只需要继续往下遍历即可。所以当这个状态激活的时候,上面说到的Target属性才会不存在。只有当这个状态没有激活的时候,我们需要在读完这个之后跳转到另一个字符,而这时target才会给出一个index值让我们去跳转。 再看看FST的源码: 这里是它的六个属性flags 这是它的构建过程(节选) 如果到这个地方还是无法理解,那也没事,后面在写到FST的构建(写)以及读的过程就会好理解很多。 下面再一步步地构建FST,通过观察构建过程和结合读过程搞懂这玩意。
new Builder()。初始化BytesStore bytes.写入一个0,用来表示FST的结束。因为读取的时候是反向读取,(先进后出)。
初始化一个长度为10的UnCompliedNode[ ] frontier
public void add(IntsRef input, T output) 这个方法主要干四件事情: 2.1,计算当前字符串和上一个字符串的公共前缀 2.2,调用freezeTail方法,从尾部一直到公共前缀的节点,将已确定的状态节点冻结 2.3,将当前字符串形成状态节点加入到frontier数组中 2.4,调整每条边的输出值 看看源码:
通过图解一步步搞清楚结点的添加过程。首先考虑输入这样一组数据构建FST 在观察过程之前,要清楚两件事情: 1.这里面的处理主要是往current数组里面填充数据,填充的是arc也就是出度。 2.处理出度的过程涉及到label,output,target,flags的填充,这里侧重点是flags的填充 Step1:ab ? Step2:abd Step3:abgl 这里由于g的顺序在d之后,发生了一些改变。 1.d状态节点被冻结住,发生了freeze Tail操作。 2.重新计算了output值 3.将node g以及node l写入forniter数组(未经处理) 但是要注意的是,此时不会将arc-d写入current数组,正如图中所说,此时尚未确定后面是否还会输入诸如abf之类的字符串。要等到节点b被冻结才会将它的出度写入current数组. Step4:acd 此时由于c的顺序在b之后,所以确定之后不会有ab前缀的输入了,此时又进行了一些操作:
node g有一个出度l. 处理node d的出度l的过程中计算了flags的值,计算过程正如图中所写。 需要注意的是这里写入了 BIT_TARGET_NEXT这个状态。 此时为什么激活呢,联想一下读取的过程,当我们读取到字母g的时候,我们的下一个应该是l,这个时候没有发生跳转而是顺序遍历下来的。所以target值是不存在的。因此这个状态要激活。 接下来处理node b node b有两个出度g和d 要对两个出度进行处理,过程基本上和处理node g的过程一样。 这里面出度g 的BIT_TARGET_NEXT状态被激活,再次联想一下读取过程,它只要是顺序读取的,就会被激活。 出度d同上(原作者似乎是计算错误了,d出度也是有激活的) 处理完以上三个出度,current数组的情况如下: Step5:msb 此时由于m的字典序在a之后,所以又有一系列工作。。 1.处理End Node 2.freeze Tail操作:冻结a,c,d节点 3.处理node-c上面的(arc-d)出度以及node-a上面的(arc-c,arc-b)出度 这里面由于b出度没有激活BIT_TARGET_NEXT状态,所以多了一个target(index),用作跳转. Step6:mst 此时有msbc上面的node-b节点的arc-c出度需要处理. 写入current数组 Step7:wl 这是最后一组输出了.此时需要将剩下未处理的所有出度处理完写进current数组 处理node-m节点上的arc-s出度 再处理node-w上的arc-l出度 最后处理entry-node(根节点)的三个出度:w,m,a 这是最终构建成功的FST结构 ? 最终构建完成的current数组 以上就是整个FST的构建过程(写),需要明确,以上的写入操作最终是写到.tim文件里 下面是读的过程。 读取FST的字符串.首先要明确两点 读的是什么? current数组。 读的顺序是? 从后往前。 这里演示一下,不过多展开. 我们结合状态的功能来读 ? 首先进来读的第一个字符是a,因为a处于数组的末端。 ? 此时读到它的flag为16,可以解码出它的状态值为:16 ? 16说明它有output值,并且值为2.并且其他状态都未激活; 而TARGET_NEXT优化没有激活,说明需要跳转。跳转的index值为16 它的label是97(ASCLL码对应就是字母’a’) 这就已经成功解读它的四个属性完成解码。再接下来往下读,我们根据a的提示跳转到index16去。 index16代表的字符是b,按照刚刚解读a的方法对它进行解读: flag是41,说明它的状态是32+8+1 ? ? ? 32说明它有一个final-output值,并且为7 8说明它是一个终止节点,也就是说读到这就成功取出第一个字符串’ab’ 1说明它是当前peding-term的最后一个字符. 而TARGET_NEXT优化没有激活,说明需要跳转。跳转的index值为8. 它的label是98,对应ASCLL码就是字母’b’ 如此一来我们就取出第一个字符串’ab’了. 后面字符串的读取过程可以参照此过程进行。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 1:38:55- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |