您的位置:首页 > 编程语言 > Java开发

位置信息倒排索引K词近邻搜索算法实现

2014-07-27 01:02 1346 查看
位置信息索引是在倒排索引的基础上实现的,在倒排记录表中添加了词项在文档中的位置信息。位置信息一般以下面的方式存储到倒排记录中:

文档ID:(位置1,位置2,…)
而完整的包含位置信息的倒排记录表如下图所示:



以图中一个倒排记录为例,to是此项,993427为to的文档频率,即在993427篇文档中出现。最外层的括号中,1,2,4,5,7是包含to的文档ID,这里只列出5个,文档ID后是to的词频,即to在文档中出现的次数。最后内层的括号中即to在文档中的位置信息,以对文档开头的偏移量来表示。有了这“强化”过后的倒排索引表,我们可以对倒排索引查询的功能进行扩展。而这次我带来的是基于位置信息的倒排索引K词近邻搜索算法的实现,具体的含义就是对两个词项进行搜索,可规定两个词项在文档中的间隔。

举个例子:某个查询为to be or not to be,我们只看to be,to与be是挨着的,也就是说间隔k为0个词。首先,查找同时包含这两个词项的文档;然后,检查表中的位置看看是否有某个be的前面一个词条位置上正好出现to。下面中给出的倒排记录表中,可能存在的一个匹配为:

to: 〈. . . ; 4: 〈. . . ,429,433〉; . . . 〉
be: 〈. . . ; 4: 〈 . . ,430,434〉; . . . 〉


本文中给出的算法来源于《信息检索导论》一书中,王斌翻译,书中给出了算法伪代码。另外我的算法实现是在课程结束之后完成的,JAVA写的,只是很单纯的实现,代码写得也比较差劲,不值得拿来做研究使用,只是贴出来给大家进行参考。下面给出代码和索引文件。

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import javax.annotation.PostConstruct;

public class PositionalIndex {

public PositionalIndex() {
// TODO Auto-generated constructor stub
//LTerm = new ArrayList<String>();
//LPostingList = new ArrayList<String>();
}

@SuppressWarnings("resource")
/******************** 按词条搜索出对应的倒排记录******************/
public static String findPostingList(String term)
{
List<String> LTerm = new ArrayList<>();
String szPostingList = "";
File file = new File("bin/Index.txt");				//读出倒排记录表文件
String szTermAndPosting;
String szSub;
//StringTokenizer szToken = null;
BufferedReader br =null;
int iSplitIndex;
try
{
br = new BufferedReader(new FileReader(file));
while((szTermAndPosting = br.readLine())!=null)				//按行读取出一条倒排记录
{
iSplitIndex = szTermAndPosting.indexOf(",");			//按','划分倒排记录
szSub = szTermAndPosting.substring(0, iSplitIndex);		//得到词项
szSub.trim();
if(term.equals(szSub))									//判断搜索词条与词项是否匹配
{
LTerm.add(term);
iSplitIndex = szTermAndPosting.indexOf(":");		//划分文档频率与倒排记录
szSub = szTermAndPosting.substring(iSplitIndex+1);	//得到倒排记录

//szSub = szSub.substring(iSplitIndex+1);
szPostingList += szSub;
break;
}
}
br.close();
}
catch(Exception e){}

return szPostingList;											//返回倒排记录
}

/***********************根据k值来搜索出倒排记录表中间隔小于k的文档列表**************************/
public static List<String> PosIntersect(String termAndpList_1,String termAndpList_2,int k)
{
List<String> LAns = new ArrayList<>();
StringTokenizer strToken;
List<String> szTerm_1 = new ArrayList<>(),szTerm_2 = new ArrayList<>();
List<String> szPList_1 = new ArrayList<>(),szPList_2 = new ArrayList<>();			//倒排记录
int iDocID_1,iDocID_2;
List<String> LtermAndList_1 = new ArrayList<>(),LtermAndList_2 = new ArrayList<>();

strToken = new StringTokenizer(termAndpList_1, ";");								//按';'划分倒排记录
while(strToken.hasMoreTokens())
{
LtermAndList_1.add(strToken.nextToken());										//文档id+位置信息
}

strToken = new StringTokenizer(termAndpList_2, ";");
while(strToken.hasMoreTokens())
{
LtermAndList_2.add(strToken.nextToken());
}

for(int i = 0,j = 0;i<LtermAndList_1.size()&&j<LtermAndList_2.size();)
{
strToken = new StringTokenizer(LtermAndList_1.get(i)," ");						//划分文档id+位置信息
szTerm_1.add(strToken.nextToken());
szPList_1.add(strToken.nextToken());											//位置信息

strToken = new StringTokenizer(LtermAndList_2.get(j), " ");
szTerm_2.add(strToken.nextToken());
szPList_2.add(strToken.nextToken());

int iSplitIndex = szTerm_1.get(i).indexOf(",");
iDocID_1 = Integer.parseInt(szTerm_1.get(i).substring(0, iSplitIndex));			//得到文档id
iSplitIndex = szTerm_2.get(i).indexOf(",");
iDocID_2 = Integer.parseInt(szTerm_2.get(j).substring(0, iSplitIndex));

/******************两个倒排记录根据文档id号进行比较*********************/
if(iDocID_1 == iDocID_2)														//判断倒排记录中相同的文档
{
List<String> Ltemp = new ArrayList<>();
List<String> Lposting_1 = new ArrayList<>();
List<String> Lposting_2 = new ArrayList<>();
StringTokenizer szPostingToken = new StringTokenizer(szPList_1.get(i),",");		//划分位置信息
while(szPostingToken.hasMoreTokens())
{
Lposting_1.add(szPostingToken.nextToken());
}
szPostingToken = new StringTokenizer(szPList_2.get(j),",");
while(szPostingToken.hasMoreTokens())
{
Lposting_2.add(szPostingToken.nextToken());
}
for(int p=0;p<Lposting_1.size();p++)
{
for(int q=0;q<Lposting_2.size();q++)
{
if(Math.abs(Integer.parseInt(Lposting_1.get(p))-Integer.parseInt(Lposting_2.get(q)))<=k)	//计算文档中词项之间的距离与k进行比较
{
Ltemp.add(Lposting_2.get(q));				//将与Lposting_1中位置相比符合条件的Lposting_2的位置信息进行存储
}
else if(Integer.parseInt(Lposting_2.get(q))>Integer.parseInt(Lposting_1.get(p)))
break;
}
/*for(int x=0;x<Ltemp.size()&&Math.abs(Integer.parseInt(Ltemp.get(x))-Integer.parseInt(Lposting_1.get(p)))>k;x++)
{
Ltemp.remove(0);
}*/
for(int x=0;x<Ltemp.size();x++)
{
String ansTemp = Integer.toString(iDocID_1)+","+Lposting_1.get(p)+","+Ltemp.get(x);			//将与Lposting_1比较符合条件的Ltemp中的位置信息组合成字符串进行存储
LAns.add(ansTemp);
}
Ltemp.clear();
}
i++;j++;

}
else if(iDocID_1<iDocID_2)					//iDocID_1<iDocID_2,IDocID_1的序号+1
i++;
else j++;									//同理,IDocID_2的序号+1,返回继续比较
}

return LAns;

}
/**
* @param args
* @throws IOException
*/
public static void main(String[] args){
// TODO Auto-generated method stub
String sztermAndpList_1 ;		//词条与倒排记录
String sztermAndpList_2 ;
String szReadline;
String szTerm_1;				//词条
String szTerm_2;
int k;
BufferedR
4000
eader Stdin = new BufferedReader(new InputStreamReader(System.in));
try
{
while((szReadline = Stdin.readLine())!=null&&!szReadline.equals("exit"))	//控制台输入,exit退出
{
List<String> LAnswer = new ArrayList<>();
StringTokenizer szArgsToken = new StringTokenizer(szReadline," ");		//按空格划分出词条
szTerm_1 = szArgsToken.nextToken();
szTerm_2 = szArgsToken.nextToken();
k = Integer.parseInt(szArgsToken.nextToken());							//划分出k值
sztermAndpList_1 = findPostingList(szTerm_1);							//按词条得出词项倒排记录组
sztermAndpList_2 = findPostingList(szTerm_2);
LAnswer = PosIntersect(sztermAndpList_1, sztermAndpList_2, k);			//存储结果
if(LAnswer.size()==0)													//输出
System.out.println("not found...");
for(int i=0;i<LAnswer.size();i++)
System.out.println(LAnswer.get(i));
}
Stdin.close();
}
catch(IOException e){}
}
}


下面是索引文件Index.txt截图,倒排记录间隔的符号是规定好的,方便程序中对各部分的划分。索引是手动构建的,并非使用程序构建。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐