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

来源:互联网 发布:屏幕数据库 编辑:程序博客网 时间:2024/05/01 11:30

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

文档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));//得到文档idiSplitIndex = 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的序号+1i++;else j++;//同理,IDocID_2的序号+1,返回继续比较}return LAns;}/** * @param args * @throws IOException  */public static void main(String[] args){// TODO Auto-generated method stubString sztermAndpList_1 ;//词条与倒排记录String sztermAndpList_2 ;String szReadline;String szTerm_1;//词条String szTerm_2;int k;BufferedReader 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截图,倒排记录间隔的符号是规定好的,方便程序中对各部分的划分。索引是手动构建的,并非使用程序构建。


0 0