- 浏览: 10214 次
- 性别:
- 来自: 北京
-
文章分类
最新评论
首先要声明的是, 这个代码我也参考过一个C++的实现, 不过, 他实在写的过于烦琐,一堆的模板代码, 和stl的使用。 幸好10年前摸过C/C++ 2年, 否则还真不知道他在干什么。 可惜这个代码有些致命的缺点是,字典需要生成后使用, 无法做动态的扩展。 不过呢, 动态加入一个新词, 性能是是致命的。 程序的工作模式是:
1. 通过build()函数,把所有的词生成数据,
2. 然后通过save()函数保存数据。
3. 使用的时候就可以用load()载入数据。
public class DoubelArrayTrie{
// 节点信息
private int baseArray[];
private int checkArray[];
// 保存节点已经使用
private boolean usedArray[];
private int nextCheckPos;
private int writeSize = 0;
public void build(List<char[]> wordList, PreProcess process) {
if (wordList == null) {
return;
}
int size = wordList.size();
if (size > 0) {
List<Element> elements = null;
if (process != null) {
elements = process.process(wordList);
} else {
elements = new ArrayList<Element>(wordList.size());
for (char[] cs : wordList) {
elements.add(new GenericElement(cs));
}
}
Collections.sort(elements, new CharArrayComparator<Element>());
resize(1);
baseArray[0] = 1;
nextCheckPos = 0;
Node root_node = new Node();
root_node.left = 0;
root_node.right = size;
root_node.depth = 0;
List<Node> siblings = createSiblings();
fetch(elements, root_node, siblings);
insert(elements, siblings);
size = size + (1 << 8 * 2) + 1;
if (size > usedArray.length) {
resize(size);
}
}
}
private int insert(List<Element> elements, List<Node> siblings) {
int begin = 0;
int nonZeroCount = 0;
boolean first = false;
int pos = (siblings.get(0).code + 1 > nextCheckPos ? siblings.get(0).code + 1 : nextCheckPos) - 1;
if (pos >= usedArray.length) {
resize(pos + 1);
}
while (true) {
pos++;
if (pos >= usedArray.length) {
resize(pos + 65535);
}
if (checkArray[pos] != 0) {
nonZeroCount++;
continue;
} else if (!first) {
nextCheckPos = pos;
first = true;
}
begin = pos - siblings.get(0).code;
int t = begin + siblings.get(siblings.size() - 1).code;
if (t > usedArray.length) {
resize(t + 65535);
}
if (usedArray[begin]) {
continue;
}
boolean flag = false;
for (int i = 1; i < siblings.size(); i++) {
if (checkArray[begin + siblings.get(i).code] != 0) {
flag = true;
break;
}
}
if (!flag) break;
}
if (1.0 * nonZeroCount / (pos - nextCheckPos + 1) >= 0.95) {
nextCheckPos = pos;
}
usedArray[begin] = true;
writeSize = Math.max(writeSize, begin + siblings.get(siblings.size() - 1).code + 1);
for (Node node : siblings) {
checkArray[begin + node.code] = begin;
}
for (Node node : siblings) {
List<Node> newSiblings = createSiblings();
if (fetch(elements, node, newSiblings) == 0) {
baseArray[begin + node.code] = -node.left - 1;
} else {
int ins = insert(elements, newSiblings);
baseArray[begin + node.code] = ins;
}
}
return begin;
}
private List<Node> createSiblings() {
return new ArrayList<Node>();
}
private void resize(int size) {
// checkArray array
int tmp[] = new int[size];
if (baseArray != null) {
System.arraycopy(baseArray, 0, tmp, 0, baseArray.length);
}
baseArray = tmp;
// baseArray array
int tmp1[] = new int[size];
if (checkArray != null) {
System.arraycopy(checkArray, 0, tmp1, 0, checkArray.length);
}
checkArray = tmp1;
// usedArray array
boolean tmp2[] = new boolean[size];
if (usedArray != null) {
System.arraycopy(usedArray, 0, tmp2, 0, usedArray.length);
}
usedArray = tmp2;
}
private int fetch(List<Element> words, Node parent, List<Node> siblings) {
int prev = 0;
Node preNode = null;
for (int i = parent.left; i < parent.right; i++) {
char word[] = words.get(i).getChars();
int len = word.length;
if (len < parent.depth) {
continue;
}
int cur = 0;
if (len != parent.depth) {
cur = word[parent.depth] + 1;
}
if (prev > cur) {
throw new RuntimeException("Fatal: sort dictionary first.\n");
}
if (cur != prev || siblings.size() == 0) {
Node tmpNode = new Node();
tmpNode.depth = parent.depth + 1;
tmpNode.code = cur; // 重新计算每个字的映射?
tmpNode.left = i;
if (len == parent.depth + 1) {
tmpNode.frequence = words.get(i).getFrequence();
}
if (preNode != null) {
preNode.right = i;
}
preNode = tmpNode;
siblings.add(tmpNode);
}
prev = cur;
}
if (preNode != null) {
preNode.right = parent.right;
}
return siblings.size();
}
public void save(String file) throws IOException {
DataOutputStream out = null;
int dsize = checkArray.length;
try {
out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
out.writeInt(dsize);
for (int i = 0; i < dsize; i++) {
out.writeInt(checkArray[i]);
out.writeInt(baseArray[i]);
}
out.close();
} finally {
if (out != null) {
out.close();
}
}
}
public void load(String fileName) throws IOException {
File file = new File(fileName);
DataInputStream is = null;
try {
is = new DataInputStream(new BufferedInputStream(new FileInputStream(file), 1024 * 1024));
load(is);
} finally {
if (is != null) is.close();
}
}
public void load(InputStream in) throws IOException {
DataInputStream is = new DataInputStream(new BufferedInputStream(in, 1024 * 1024));
int size = is.readInt();
checkArray = new int[size];
baseArray = new int[size];
for (int i = 0; i < size; i++) {
checkArray[i] = is.readInt();
baseArray[i] = is.readInt();
}
}
public int search(String key) {
return search(key.toCharArray(), 0, key.length());
}
public int search(char key[], int pos, int len) {
if (len == 0) {
len = key.length;
}
int b = baseArray[0];
int p;
for (int i = pos; i < len; i++) {
p = b + key[i] + 1;
if (b == checkArray[p]) {
b = baseArray[p];
} else {
return -1;
}
}
p = b;
int n = baseArray[p];
if (b == checkArray[p] && n < 0) {
return -n - 1;
}
return -1;
}
public List<Word> prefixSearch(char[] key, int pos, int len) {
int p, n, i, b = baseArray[0];
List<Word> result = new ArrayList<Word>();
for (i = pos; i < len; ++i) {
p = b; // + 0;
n = baseArray[p];
if (b == checkArray[p] && n < 0) {
Word w = new Word();
w.position = -n - 1;
w.begin = pos;
w.length = i - pos;
result.add(w);
}
p = b + (key[i]) + 1;
if (b == checkArray[p]) {
b = baseArray[p];
} else {
return result;
}
}
p = b;
n = baseArray[p];
if (b == checkArray[p] && n < 0) {
Word w = new Word();
w.position = -n - 1;
w.begin = pos;
w.length = i - pos;
result.add(w);
}
return result;
}
public Word prefixSearchMax(char[] key, int pos, int len) {
int p, n, i, b = baseArray[0];
Word w = null;
for (i = pos; i < pos + len; ++i) {
p = b; // + 0;
n = baseArray[p];
if (b == checkArray[p] && n < 0) {
if (w == null) {
w = new Word();
}
w.position = -n - 1;
w.begin = pos;
w.length = i - pos;
}
p = b + (key[i]) + 1;
if (b == checkArray[p]) {
b = baseArray[p];
} else {
return w;
}
}
p = b;
n = baseArray[p];
if (b == checkArray[p] && n < 0) {
if (w == null) {
w = new Word();
}
w.position = -n - 1;
w.begin = pos;
w.length = i - pos;
}
return w;
}
//字符数组比较子
public class CharArrayComparator<T> implements Comparator<T> {
public int compare(T o1, T o2) {
char[] a = ((Element) o1).getChars();
char[] b = ((Element) o2).getChars();
int loop = a.length > b.length ? b.length : a.length;
for (int i = 0; i < loop; i++) {
int c = a[i] - b[i];
if (c != 0) {
return c;
}
}
return a.length - b.length;
}
}
//在生成数据前, 这个接口实现了特定的数据处理
public interface PreProcess {
public List<Element> process(List<char[]> lines);
}
里面还有一些简单的数据结构, 当然这些都不是必然需要的, 我为了自己的业务需求, 实现了一些特定的数据结构。 当然, 这个版本我已经删除了我的业务代码, 可能会编译通过不了。 但是, 所有BUG已经被修正了。
1. 通过build()函数,把所有的词生成数据,
2. 然后通过save()函数保存数据。
3. 使用的时候就可以用load()载入数据。
public class DoubelArrayTrie{
// 节点信息
private int baseArray[];
private int checkArray[];
// 保存节点已经使用
private boolean usedArray[];
private int nextCheckPos;
private int writeSize = 0;
public void build(List<char[]> wordList, PreProcess process) {
if (wordList == null) {
return;
}
int size = wordList.size();
if (size > 0) {
List<Element> elements = null;
if (process != null) {
elements = process.process(wordList);
} else {
elements = new ArrayList<Element>(wordList.size());
for (char[] cs : wordList) {
elements.add(new GenericElement(cs));
}
}
Collections.sort(elements, new CharArrayComparator<Element>());
resize(1);
baseArray[0] = 1;
nextCheckPos = 0;
Node root_node = new Node();
root_node.left = 0;
root_node.right = size;
root_node.depth = 0;
List<Node> siblings = createSiblings();
fetch(elements, root_node, siblings);
insert(elements, siblings);
size = size + (1 << 8 * 2) + 1;
if (size > usedArray.length) {
resize(size);
}
}
}
private int insert(List<Element> elements, List<Node> siblings) {
int begin = 0;
int nonZeroCount = 0;
boolean first = false;
int pos = (siblings.get(0).code + 1 > nextCheckPos ? siblings.get(0).code + 1 : nextCheckPos) - 1;
if (pos >= usedArray.length) {
resize(pos + 1);
}
while (true) {
pos++;
if (pos >= usedArray.length) {
resize(pos + 65535);
}
if (checkArray[pos] != 0) {
nonZeroCount++;
continue;
} else if (!first) {
nextCheckPos = pos;
first = true;
}
begin = pos - siblings.get(0).code;
int t = begin + siblings.get(siblings.size() - 1).code;
if (t > usedArray.length) {
resize(t + 65535);
}
if (usedArray[begin]) {
continue;
}
boolean flag = false;
for (int i = 1; i < siblings.size(); i++) {
if (checkArray[begin + siblings.get(i).code] != 0) {
flag = true;
break;
}
}
if (!flag) break;
}
if (1.0 * nonZeroCount / (pos - nextCheckPos + 1) >= 0.95) {
nextCheckPos = pos;
}
usedArray[begin] = true;
writeSize = Math.max(writeSize, begin + siblings.get(siblings.size() - 1).code + 1);
for (Node node : siblings) {
checkArray[begin + node.code] = begin;
}
for (Node node : siblings) {
List<Node> newSiblings = createSiblings();
if (fetch(elements, node, newSiblings) == 0) {
baseArray[begin + node.code] = -node.left - 1;
} else {
int ins = insert(elements, newSiblings);
baseArray[begin + node.code] = ins;
}
}
return begin;
}
private List<Node> createSiblings() {
return new ArrayList<Node>();
}
private void resize(int size) {
// checkArray array
int tmp[] = new int[size];
if (baseArray != null) {
System.arraycopy(baseArray, 0, tmp, 0, baseArray.length);
}
baseArray = tmp;
// baseArray array
int tmp1[] = new int[size];
if (checkArray != null) {
System.arraycopy(checkArray, 0, tmp1, 0, checkArray.length);
}
checkArray = tmp1;
// usedArray array
boolean tmp2[] = new boolean[size];
if (usedArray != null) {
System.arraycopy(usedArray, 0, tmp2, 0, usedArray.length);
}
usedArray = tmp2;
}
private int fetch(List<Element> words, Node parent, List<Node> siblings) {
int prev = 0;
Node preNode = null;
for (int i = parent.left; i < parent.right; i++) {
char word[] = words.get(i).getChars();
int len = word.length;
if (len < parent.depth) {
continue;
}
int cur = 0;
if (len != parent.depth) {
cur = word[parent.depth] + 1;
}
if (prev > cur) {
throw new RuntimeException("Fatal: sort dictionary first.\n");
}
if (cur != prev || siblings.size() == 0) {
Node tmpNode = new Node();
tmpNode.depth = parent.depth + 1;
tmpNode.code = cur; // 重新计算每个字的映射?
tmpNode.left = i;
if (len == parent.depth + 1) {
tmpNode.frequence = words.get(i).getFrequence();
}
if (preNode != null) {
preNode.right = i;
}
preNode = tmpNode;
siblings.add(tmpNode);
}
prev = cur;
}
if (preNode != null) {
preNode.right = parent.right;
}
return siblings.size();
}
public void save(String file) throws IOException {
DataOutputStream out = null;
int dsize = checkArray.length;
try {
out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
out.writeInt(dsize);
for (int i = 0; i < dsize; i++) {
out.writeInt(checkArray[i]);
out.writeInt(baseArray[i]);
}
out.close();
} finally {
if (out != null) {
out.close();
}
}
}
public void load(String fileName) throws IOException {
File file = new File(fileName);
DataInputStream is = null;
try {
is = new DataInputStream(new BufferedInputStream(new FileInputStream(file), 1024 * 1024));
load(is);
} finally {
if (is != null) is.close();
}
}
public void load(InputStream in) throws IOException {
DataInputStream is = new DataInputStream(new BufferedInputStream(in, 1024 * 1024));
int size = is.readInt();
checkArray = new int[size];
baseArray = new int[size];
for (int i = 0; i < size; i++) {
checkArray[i] = is.readInt();
baseArray[i] = is.readInt();
}
}
public int search(String key) {
return search(key.toCharArray(), 0, key.length());
}
public int search(char key[], int pos, int len) {
if (len == 0) {
len = key.length;
}
int b = baseArray[0];
int p;
for (int i = pos; i < len; i++) {
p = b + key[i] + 1;
if (b == checkArray[p]) {
b = baseArray[p];
} else {
return -1;
}
}
p = b;
int n = baseArray[p];
if (b == checkArray[p] && n < 0) {
return -n - 1;
}
return -1;
}
public List<Word> prefixSearch(char[] key, int pos, int len) {
int p, n, i, b = baseArray[0];
List<Word> result = new ArrayList<Word>();
for (i = pos; i < len; ++i) {
p = b; // + 0;
n = baseArray[p];
if (b == checkArray[p] && n < 0) {
Word w = new Word();
w.position = -n - 1;
w.begin = pos;
w.length = i - pos;
result.add(w);
}
p = b + (key[i]) + 1;
if (b == checkArray[p]) {
b = baseArray[p];
} else {
return result;
}
}
p = b;
n = baseArray[p];
if (b == checkArray[p] && n < 0) {
Word w = new Word();
w.position = -n - 1;
w.begin = pos;
w.length = i - pos;
result.add(w);
}
return result;
}
public Word prefixSearchMax(char[] key, int pos, int len) {
int p, n, i, b = baseArray[0];
Word w = null;
for (i = pos; i < pos + len; ++i) {
p = b; // + 0;
n = baseArray[p];
if (b == checkArray[p] && n < 0) {
if (w == null) {
w = new Word();
}
w.position = -n - 1;
w.begin = pos;
w.length = i - pos;
}
p = b + (key[i]) + 1;
if (b == checkArray[p]) {
b = baseArray[p];
} else {
return w;
}
}
p = b;
n = baseArray[p];
if (b == checkArray[p] && n < 0) {
if (w == null) {
w = new Word();
}
w.position = -n - 1;
w.begin = pos;
w.length = i - pos;
}
return w;
}
//字符数组比较子
public class CharArrayComparator<T> implements Comparator<T> {
public int compare(T o1, T o2) {
char[] a = ((Element) o1).getChars();
char[] b = ((Element) o2).getChars();
int loop = a.length > b.length ? b.length : a.length;
for (int i = 0; i < loop; i++) {
int c = a[i] - b[i];
if (c != 0) {
return c;
}
}
return a.length - b.length;
}
}
//在生成数据前, 这个接口实现了特定的数据处理
public interface PreProcess {
public List<Element> process(List<char[]> lines);
}
里面还有一些简单的数据结构, 当然这些都不是必然需要的, 我为了自己的业务需求, 实现了一些特定的数据结构。 当然, 这个版本我已经删除了我的业务代码, 可能会编译通过不了。 但是, 所有BUG已经被修正了。
相关推荐
NULL 博文链接:https://show-your-sister.iteye.com/blog/277286
### 基于双数组Trie树中文分词研究 #### 概述 本文献针对中文信息处理中的分词问题,研究了一种基于双数组Trie树(Double-Array Trie, DAT)的优化方法。传统的分词算法面临着空间利用率低、插入速度慢等问题,...
总的来说,Java实现的中文分词SimHash算法结合了Sanford分词库的分词功能和SimHash的相似度检测,为中文文本的相似度分析提供了一种高效且准确的方法。在实际应用中,这种技术广泛应用于搜索引擎的去重、推荐系统、...
Java实现的中文分词算法是自然语言处理领域中的重要技术,尤其在文本挖掘、搜索引擎、信息检索等场景中发挥着关键作用。FMM(Fast Mapping Model)和BMM(Bigram Mapping Model)是两种常见的中文分词算法,它们都是...
中文分词_基于互信息+邻接信息熵实现的中文分词算法_附项目源码_优质项目实战
word分词是一个Java实现的中文分词组件,提供了多种基于词典的分词算法,并利用ngram模型来消除歧义。 能准确识别英文、数字,以及日期、时间等数量词,能识别人名、地名、组织机构名等未登录词。 同时提供了Lucene...
综上所述,CQ V2.0分词系统基于双数组Trie树和Bates策略,实现了高效、准确的分词效果。其开源特性使得这个系统更加透明且具有可扩展性,为自然语言处理领域的研究和应用提供了有力的支持。通过深入学习和实践,我们...
分词算法 汉语分词介绍分词算法 汉语分词介绍分词算法 汉语分词介绍分词算法 汉语分词介绍分词算法 汉语分词介绍分词算法 汉语分词介绍分词算法 汉语分词介绍分词算法 汉语分词介绍分词算法 汉语分词介绍分词算法 ...
Java实现的中文分词程序是一种基于Java编程语言的文本处理工具,主要应用于处理中文文本,将其拆分成有意义的词汇单元,这一过程被称为分词。在自然语言处理(NLP)领域,分词是预处理阶段的关键步骤,为后续的文本...
### 双数组Trie树算法优化及其应用研究 #### 摘要与关键词解析 本文主要探讨了一种针对双数组Trie树(Double-Array Trie)算法的优化策略,并通过实验验证了该策略的有效性。双数组Trie树是一种高效的数据结构,常...
在提供的压缩包文件中,包含了各种与分词相关的源码,例如"zt_逆向最大匹配分词算法"可能是实现逆向最大匹配算法的具体代码,"秒盘古分词"可能是指快速版本的盘古分词程序,"中文分词"和"英文分词"源码分别针对中文...
在这个标题为“不依赖第三方的java分词算法”的项目中,开发者提供了一个独立的Java实现,旨在避免对其他外部库的依赖,使得在进行分词操作时更加灵活和高效。 分词算法通常有多种,其中提到的“正向最大匹配”...
1. **查询速度:** 通过比较不同索引机制下的词典查询速度,发现采用优化后的双数组Trie树算法构建的词典在查询速度上明显优于其他机制。 2. **空间占用:** 测试结果显示,即使在数据量较大的情况下,优化后的双数组...
### TF-IDF算法Java实现详解 #### 一、算法简介 TF-IDF(Term Frequency-Inverse Document Frequency)是一种常用于信息检索与文本挖掘中的权重计算公式。它通过统计单词在文档中出现的频率以及在整个文集中的频率...
### 中文词库与分词算法:构建高效自然语言处理基石 #### 一、中文词库的重要性 在自然语言处理(NLP)领域,中文词库扮演着至关重要的角色。与英文等西方语言不同,中文没有明确的词界标记,这使得中文文本的切分...
中文自动分词算法 中文自动分词算法是自然语言处理中的一项基本技术,旨在将中文文本切分成单个词语,以便更好地进行信息检索、自动标引、自动文摘、机器翻译、语言学研究、搜索引擎研究和自然语言理解等领域的应用...
1. jieba-java:移植自Python的jieba分词库,支持精确模式、全模式、搜索引擎模式等多种分词方式。 2. HanLP:由福州大学与自然语言处理实验室开发,提供了分词、词性标注、命名实体识别等功能。 3. IK Analyzer:...
ICTCLAS分词的实现案例,完整的使用java代码实现,可以直接导入工程运行。
IK Analyzer 是一个开源的,基于 java 语言开发的轻量级的中文分词工具包。从 2006年 12 月推出 1.0 版... 在 2012 版本中,IK 实现了简单的分词歧义排除算法,标志着 IK 分词器从单纯的词典分词向模拟语义分词衍化。