FSA
FSA:确定无环有限状态接收机(Deterministric acyclic finite state acceptor, FSA) tire-tree就是这种
(构建工具http://examples.mikemccandless.com/fst.py)
FST
fst: Finite State Transducers(有限状态传感器)
(构建工具http://examples.mikemccandless.com/fst.py)
图中输入框里面 单词代表输入 后面的数字代表状态
用单词遍历这个有向无环图,初始状态是0,遇到的数字就累加上,到达终点节点的话就是对应的结果状态,如果没有到达终点代表咱们这里不支持这个输入
红色的边代表可优化存储的边 a large reduction of the FST size(实际实现中它的到达节点就是它的下一个节点,这个和实际存储实现由很大关系 )
加粗的边代表下个节点是终结(这个地方注意 黑色的有加粗的 红色的也有 0/1 g/1 这两条边都是加粗的)
构建方法:
输入的词是有序的(有序的构建的是最小树),添加词,再添加词(锁定部分边有些是不会动的边了 可以锁定了)调整未锁定的边,同时调整里面的数值让输出是目标值。再添加的时候有些边又可以锁定了,然后调整其他未锁定的。
输出状态可以是数字大于等于0的整数:每个路,经边的值加和。
输出状态可以是文本:每个路,经过的值得合并,下面以上图为例 Lucene 的fst为例进行说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| 目的是使用1维的byte数组将状态变化全部保存起来
上图拉伸变形后
c a t
d e e p
o g s
e g g
镜像后得到(镜像是和插入顺序相关)
c d e g g
o g s
e e p
a t
从后到前依次填入到byte里面
fst的bytes字段
#一行10个byte 10进制
0 116 15 97 6 112 15 101 6 1
115 31 1 103 23 1 111 23 8 101
0 103 15 103 6 6 101 22 20 2
100 16 4 1 99 16
#转换arc为字母 标记状态
(0) [t] 15 [a] 6 [p] 15 [e] 6 <1>
[s] 31 <1> [g] 23 <1> [o] 23 {8} [e]
0 [g] 15 [g] 6 <6> [e] 22 {20} <2>
[d] 16 {4} <1> [c] 16
lucene 的arc 标记位8个bit定义了6个标记
BIT_FINAL_ARC = 1 << 0; 表示此边的目标节点是否为可接受状态
BIT_LAST_ARC = 1 << 1; 表示此边是节点的最后一条边,此边后面的内容就是下一个节点了。一个节点的所有边按顺序写入到bytes中,只有最后一条边被标记为BIT_LAST_ARC
BIT_TARGET_NEXT = 1 << 2; 表示此边应用了target_next优化,bytes中的下一个Node就是此边的目标节点
BIT_STOP_NODE = 1 << 3; 表示此边的目标节点是-1,已经遍历到了FST的末尾。
BIT_ARC_HAS_OUTPUT = 1 << 4; 表示此边有输出
BIT_ARC_HAS_FINAL_OUTPUT = 1 << 5; 则该ARC需要存储output信息。
BIT_MISSING_ARC = 1 << 6; 表示此边的目标节点有输出(final output)
flag=16,二进制为 00010000 。
第三位为BIT_TARGET_NEXT=0,因此有目标节点target;
第五位为BIT_ARC_HAS_OUTPUT=1,因此有输出符号output
flag=22, 二进制为 00010110 。
第二位BIT_LAST_ARC=1,说明这是节点的最后一条边;
第三位BIT_TARGET_NEXT=1,因此不需要存储target,其目标节点为下一个节点 往前找就可以了;
第五位为BIT_ARC_HAS_OUTPUT=1,因此有输出符号output
用的时候从后往前查找 比如查找 do
(0) 代表结束
[] 代表arc
<> 代表输出
{} 代表目标跳转
纯数字 代表flag |
应用场景:
1.搜索功能的sug模块: tire-tree和 fst均可作为底层数据结构,实现产品效果是一样的,查询效率相同都是 o(length(queryword))。但是fst的索引构建相较tire的复杂,tire的使用的内存较多,不能共享尾部。对于中文sug来转化为拼音和拼音前缀的Tire很浪费内存。【这个地方重点是压缩率】sug不用这些结构也是可以的(提前构建好用hashmap也可以)
2.Lucene的 term和 doc id的映射就是用的fst。
3.拼写检查
lucence fst使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| public static void main (String[] args ) throws Exception {
/*
参考文档
http://lucene.apache.org/core/8_2_0/core/org/apache/lucene/util/fst/package-summary.html
*/
String inputValues [] = {"cat", "deep", "do", "dog", "dogs", "egg", };
long outputValues [] = {1, 2, 3, 4, 5, 6};
PositiveIntOutputs outputs = PositiveIntOutputs. getSingleton();
Builder <Long > builder = new Builder <Long >(FST. INPUT_TYPE. BYTE1, outputs );
IntsRefBuilder scratchInts = new IntsRefBuilder ();
for (int i = 0; i < inputValues. length; i ++) {
BytesRef bb = new BytesRef (inputValues [i ]);
System. out. println(bb. toString());
builder. add(Util. toIntsRef(bb, scratchInts ), outputValues [i ]);
}
FST <Long > fst = builder. finish();
System. out. println(fst. toString());
//find-----------------------------------
BytesRef xx = new BytesRef ("cat");
System. out. println("-------------");
System. out. println("search result:"+Util. get(fst, xx ));
System. out. println("-------------");
System. out. println("foreach fst:");
//foreach-------------------------------
IntsRefFSTEnum <Long > fstEnum = new IntsRefFSTEnum <Long >(fst );
IntsRefFSTEnum. InputOutput<Long > inputOutput ;
BytesRefBuilder scratch = new BytesRefBuilder ();
while ((inputOutput = fstEnum. next()) != null) {
String input = Util. toBytesRef(inputOutput. input, scratch ). utf8ToString();
System. out. println(input + "\t" + inputOutput. output);
}
//sug-------------------------------------
System. out. println("-------------\nsug result:");
String forsug = "do";
BytesRef bytesRef = new BytesRef (forsug );
IntsRef input = Util. toIntsRef(bytesRef, new IntsRefBuilder ());
FST. Arc<Long > arc = fst. getFirstArc(new FST. Arc<Long >());
FST. BytesReader fstReader = fst. getBytesReader();
for (int i = 0; i < input. length; i ++) {
if (fst. findTargetArc(input. ints[input. offset + i ], arc, arc, fstReader ) == null) {
System. out. println("can not find ...");
return;
}
}
System. out. println(arc. toString());
//"do" find top 5 shortest path
Util. TopResults<Long > results = Util. shortestPaths(fst, arc, PositiveIntOutputs. getSingleton(). getNoOutput(), new Comparator <Long >() {
@Override
public int compare (Long o1, Long o2 ) {
return o1. compareTo(o2 );
}
}, 5, false);
BytesRefBuilder bytesRefBuilder = new BytesRefBuilder ();
for (Util. Result<Long > result : results ) {
IntsRef intsRef = result. input;
System. out. println( forsug + Util. toBytesRef(intsRef, bytesRefBuilder ). utf8ToString());
}
} |
其他字典树:
Array/List 使用二分法查找,不平衡
HashMap/TreeMap 性能高,内存消耗大,几乎是原始数据的三倍
Skip List 跳跃表,可快速查找词语,在lucene、redis、Hbase等均有实现。相对于TreeMap等结构,特别适合高并发场景(Skip List介绍)
Trie 适合英文词典,如果系统中存在大量字符串且这些字符串基本没有公共前缀,则相应的trie树将非常消耗内存(数据结构之trie树)
Double Array Trie 适合做中文词典,内存占用小,很多分词工具均采用此种算法(An Implementation of Double-Array Trie : https://linux.thai.net/~thep/datrie/datrie.html)
Ternary Search Tree 三叉树,每一个node有3个节点,兼具省空间和查询快的优点(https://en.wikipedia.org/wiki/Ternary_search_tree)
Finite State Transducers (FST) 一种有限状态转移机,Lucene 4有开源实现,并大量使用
FST的开源实现
1. OpenFst, an open-source library for FST operations.
2. Stuttgart Finite State Transducer Tools, another open-source FST toolkit
3. java FST Framework, an open-source java FST Framework capable of handling OpenFst text format.
4. Vcsn, an open-source platform (C++ & IPython) platform for weighted automata and rational expressions.
5. Finite State Morphology–The Book XFST/ LEXC, a description of Xerox’s implementation of finite-state transducers intended for linguistic applications.
6. FOMA, an open-source implementation of most of the capabilities of the Xerox XFST/ LEXC implementation.
【one more thing】
利用Lucene的开源工具实现了一个小的中文sug的demo:sugdemo
参考资料:
lucene字典实现原理——FST https://www.cnblogs.com/bonelee/p/6226185.html
关于Lucene的词典FST深入剖析 http://www.shenyanchao.cn/blog/2018/12/04/lucene-fst/
https://blog.csdn.net/zx2011302580235/article/details/88594342
fst wiki https://en.wikipedia.org/wiki/Finite-state_transducer