import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;//只能处理a-z+空格
public class ShingleSet  {String filepath;int k;int signatureNumber;int times;int [] randomArray;int [] randomArrayForLSH;ArrayList <String> array=new ArrayList <String>();//存储所有的Shingles的集合,这些Shingles是无序的/**数组中的值是哈希桶的编号,即各个Shingles对应的桶号,和Shingles在array中的顺序相同但也是无序的,可以看做矩阵的行号,* * 但这些行号并没有按从小到大排序* * 从array变到resultOfHashToShingle的过程采用了相同的哈希函数** 如果是多篇文档的话,各自的resultOfHashToShingle数组中存储的桶号并不相同,也没有按照桶号的顺序来存储,仅仅存储了文* * 档的shingles都被哈希到了哪些桶* */long [] resultOfHashToShingle;long [] signature;//这个数组用于存储文本的签名矩阵int bandNumber;int [] bucketNumber;//这个数组用于存储签名被哈希到的桶号int [] bucketNumberANDOR;//这个数组用于存储签名被哈希到的桶号BufferedReader  inputStream;public ShingleSet (String filepath,int k,int signatureNumber,int bandNumber,int times, int[] randomArray,int [] randomArrayForLSH){this.filepath=filepath;   this.k=k;  this.signatureNumber=signatureNumber;this.bandNumber=bandNumber;this.times=times;this.randomArray=randomArray;this.randomArrayForLSH=randomArrayForLSH;}public void createShingleSet() {try{inputStream =new BufferedReader(new FileReader(filepath));String line=inputStream.readLine();Pattern p = Pattern.compile("\\s+|\t|\r|\n");//去掉读入行的空格,制表,换行,回车while(line!=null){Matcher m = p.matcher(line);line = m.replaceAll(" ");if(!(line.length()<k)){//这块的处理有点粗糙,行过短的被忽略,并且是先读入行再进行去除制表符、回车等字符int start =0;String tmp=null;do{tmp=line.substring(start,start+(k-1));start++;if(!array.contains(tmp)){array.add(tmp);//如果文档长度不同的话,自己所包含的shingle种类大小可能也不同}}while(!(start>line.length()-(k-1)));}line=inputStream.readLine();}}catch(FileNotFoundException e){System.out.println("文档打开出错");}catch(IOException e){System.out.println("文档读取出错");}}public void hashToShingle(){resultOfHashToShingle=new long[array.size()];for(int i=0;i<array.size();i++){String tmp=array.get(i);long sum=0;//设为longfor(int t=0;t<k-4;t++){char[] chartmp=tmp.substring(t,t+4).toCharArray();//将九位字符串中的连续四位以字符数组的形式存储//将字符串转化为32位整数。这里的强制类型转换将char转为int,再将double转long时,由于double此时为整数且不大于long最大值,所以转换无损long  inttmp=(long)((int)chartmp[0]*Math.pow(128,3)+(int)chartmp[1]*Math.pow(128,2)+(int)chartmp[2]*Math.pow(128,1)+(int)chartmp[3]*Math.pow(128,0));sum+=inttmp;}long hashResult=(sum%(long)Math.pow(2,32));//java中模运算的操作数范围大;将字符串哈希到2^32个桶中,而int占-2^31到+2^31。但桶数目小于27^9//hashResult的结果是0-2^32-1resultOfHashToShingle[i]=hashResult;}}/**对所有的桶重新进行大量哈希,每个哈希取最小的桶号* * 强制没和哈希函数的结果共有27^9个桶(每个哈希函数的桶数目可以不一样吗?)因为27^9中字符串* */public void produceSignature(){signature=new long[signatureNumber];for(int i=0;i<signatureNumber;i++){long min=(long)Math.pow(27, k);//一个哈希函数将resultOfHashToShingle中的桶号在重新排序到27^k个桶中,找出最小的桶号即为签名存储进signature即可for(int t=0;t<resultOfHashToShingle.length;t++){long tmp=(resultOfHashToShingle[t]*randomArray[2*i]+randomArray[2*i+1])%(long)Math.pow(27, k);//结果是0-27^kif(tmp<min) min=tmp;}signature[i]=min;}}    public void localitySensitiveHahing(){int rows=signatureNumber/bandNumber;//因为有bandNumber个行条,所以使得哈希函数也有bandNumber*time个桶。同一个行条必须使用同一个哈希函数。//这里不同行条使用了不同的hash函数//所以,第i个行条的哈希值=[(行条内签名之和)*randomArray[row*i]+randomArray(row*i+1)]%(bandNumber*time)//对一个文档的签名向量的每个行条使用一个哈希函数,并存入了数组bucketNumber,对每篇文档的签明进行了bandNumber次hashbucketNumber=new int[bandNumber];for(int i=0;i<bandNumber;i++){int begin=i*rows;int end=(i+1)*rows;long sum=0;for(int t=begin;t<end;t++)  sum+=signature[t];//将本文档的第i行条的哈希值(即被哈希到的桶号)放入bucketNumber[i],如果两个文档的bucketNumber[i]相等,这说明这两个文档的第i个行条完全一样//每个行条一组桶。bucketNumber[i]=(int)((sum*randomArray[rows*i]+randomArray[rows*i+1])%(bandNumber*times));}//与构造+或构造,选用的hash函数并不一定要是在局部敏感哈希中使用过的哈希函数。所以在再这里再构造4*4*bucketNumber个哈希函数对文档进行重新处理//也就是对每个行条是用来16个hash函数//每个行条使用不同的hash函数,并将结果存入数组,每篇文档进行了4*4*bandNumber次哈希。bucketNumberANDOR=new int[4*4*bandNumber];for(int i=0;i<bandNumber;i++){int begin=i*rows;int end=(i+1)*rows;long sum=0;for(int t=begin;t<end;t++)  sum+=signature[t];for(int k=0;k<(4*4);k+=2) bucketNumberANDOR[(4*4)*i+k]=(int)((sum*randomArrayForLSH[(4*4)*i+k]+randomArrayForLSH[(4*4)*i+k+1])%(bandNumber*times));}}public void run() {this.createShingleSet();this.hashToShingle();this.produceSignature();this.localitySensitiveHahing();}public static void main(String[] args){int bandNumber=1;int times=100;int signatureNumber=100;double  Jaccard;int [] randomArray;Scanner keyboard=new Scanner(System.in);//产生最小哈希签名的哈希函数数目强制设初始化为100个(100对随机数),即每个文本有100个签名,下边进行重新赋值。System.out.println("请问您希望将使用多少个Hash函数用于为文档产生签名?");signatureNumber=keyboard.nextInt();randomArray=new int [signatureNumber*2];Random random = new Random();for(int i=0;i<signatureNumber;i++){int tmp=(int)Math.pow(signatureNumber,0.5);randomArray[2*i]=(Math.abs(random.nextInt())%tmp)+1;//随机数在0-(tmp-1),改为1-tmprandomArray[2*i+1]=(Math.abs(random.nextInt())%tmp)+1;}//根据签名向量的长度以及预期的相似度来确定行条的数目,对double进行了运算,可能产生误差System.out.println("请问您希望将相似度为多少的文档在LSH过程中尽可能成为后选对?");Jaccard=keyboard.nextDouble();System.out.println("请问您希望在LSH过程中哈希桶的数目是行条数的几倍?");times=keyboard.nextInt();keyboard.close();double difference=Math.abs(Math.pow(1.0/1.0,1.0/100.0)-Jaccard); for(int i=2;i<=signatureNumber;i++){if(signatureNumber%i==0){double tmp=Math.abs(Math.pow((double)1/(double)i,(double)i/(double)signatureNumber)-Jaccard);System.out.printf("行条=%4d时  ",i);System.out.printf("差值为%8f",tmp);if(tmp<difference) {difference=tmp;bandNumber=i;System.out.println("   行条被改变");}else{System.out.println("   行条未改变");}}}System.out.println("签名矩阵被分为了"+bandNumber+"个行条");int [] randomArrayForLSH=new int [4*4*bandNumber];for(int i=0;i<(4*4*bandNumber);i++){//将所需hash数目开方得出的数字作为mod后的值,因这样使得mod后的值尽量小,同时从概率角度认为恰好可以产生足够个不同的hash函数//运行测试程序过程中出现过F2产生的相同行条相等情况多于F产生的。可能的原因是出现了系数使得出现hash冲突,而这系数被使用了四次。但是//增加hash系数的范围似乎并不能避免这种系数相同的情况//在签名足够多时也可能无法区分,原因如上。不过会不会系数并没连续使用四次,而仅仅是因为四对系数均哈希冲突//在签名数目过少时也会出现F2无法鉴别不同行的情况,原因可能是签名少,所以行条少,所以系数的范围小,所以系数被重复使用。所以“与”无效//当行条少时,桶的数目也会变少int tmp=(int)Math.pow(4*4*bandNumber,0.5 );randomArrayForLSH[i]=(Math.abs(random.nextInt())%tmp)+1;//随机数在0-(tmp-1),改为1-tmp}ShingleSet  test1=new ShingleSet ("C:\\Users\\fujiaxiaoshao\\Desktop\\test1.txt",5,signatureNumber,bandNumber,times,randomArray,randomArrayForLSH);ShingleSet  test2=new ShingleSet ("C:\\Users\\fujiaxiaoshao\\Desktop\\test2.txt",5,signatureNumber,bandNumber,times,randomArray,randomArrayForLSH);test1.run();test2.run();System.out.println("文档的签名为:");for(int i=0;i<signatureNumber;i++){System.out.printf("%5d",(i+1));System.out.printf("%20d",test1.signature[i]);System.out.printf("%20d",test2.signature[i]);System.out.println("");}for(int i=0;i<signatureNumber;i++){if(test1.signature[i]==test2.signature[i])System.out.printf("第%3d个签名相等,签名为:%-13d位于第%d个行条\n",(i+1),test1.signature[i],((i/(signatureNumber/bandNumber))+1));}System.out.println("\n\n使用"+bandNumber+"个哈希函数,每个哈希函数的桶数目是行条数的"+times+"倍,每个哈希函数hash一个行条:");int  countF=0;for(int i=0;i<bandNumber;i++){if(test1.bucketNumber[i]==test2.bucketNumber[i]){countF++;System.out.printf("\n在第"+(i+1)+"个行条中,两个文档都被哈希到了第"+test1.bucketNumber[i]+"个桶中\n");System.out.printf("在第"+(i+1)+"个行条中的所有签名为:\n");long sumOfTest1=0;long sumOfTest2=0;for(int t=(signatureNumber/bandNumber)*i;t<(signatureNumber/bandNumber)*(i+1);t++){System.out.printf("%5d",t+1);sumOfTest1+=test1.signature[t];System.out.printf("%20d",test1.signature[t]);sumOfTest2+=test2.signature[t];System.out.printf("%20d\n",test2.signature[t]);}System.out.printf("和:");System.out.printf("%d",sumOfTest1);System.out.printf("   %d\n",sumOfTest2);System.out.printf("模:");System.out.printf("%d  %d\n",(sumOfTest1*randomArray[(signatureNumber/bandNumber)*i]+randomArray[(signatureNumber/bandNumber)*i+1])%(times*bandNumber),(sumOfTest2*randomArray[(signatureNumber/bandNumber)*i]+randomArray[(signatureNumber/bandNumber)*i+1])%(times*bandNumber));}}System.out.println("\n使用"+(4*4*bandNumber)+"个哈希函数,每个哈希函数的桶数目是行条数的"+times+"倍,"+(4*4)+"个哈希函数hash一个行条:");int countF2=0;for(int k=0;k<bandNumber;k++){int count=0;for(int i=0;i<(4*4);i+=4){if(test1.bucketNumberANDOR[(4*4)*k+i]==test2.bucketNumberANDOR[(4*4)*k+i]&&test1.bucketNumberANDOR[(4*4)*k+i+1]==test2.bucketNumberANDOR[(4*4)*k+i+1]&&test1.bucketNumberANDOR[(4*4)*k+i+2]==test2.bucketNumberANDOR[(4*4)*k+i+2]&&test1.bucketNumberANDOR[(4*4)*k+i+3]==test2.bucketNumberANDOR[(4*4)*k+i+3])count++;}if(count!=0) {countF2++;System.out.printf("在第%5d个行条中,两个文档的签名很大概率保证完全相同\n",(k+1));}}System.out.printf("\n使用F时,得到相同的行条为:"+countF+"个\n使用F2时,得到相同的行条为:"+countF2+"个");}}

关于三个参数的的设置问题:

1.使用多少个Hash函数用于为文档产生签名?

使用的哈希函数不能太少,少量的哈希函数产生的签名可能并不足以代表文档本身。也就是说当每篇文档的签名太少时,两篇文档签名的相似度和文档本身的相似度或许并不相符。

使用的哈希函数不能太多,如果哈希函数太多,可能导致签名矩阵的行数比文档的shingles集合矩阵的行数还要多,也就是说,在将shingles集合压缩成签名的过程并没有使得矩阵变小反而增大。

2.将相似度为多少的文档在LSH过程中尽可能成为后选对?

如果希望将相似度0.8的文档成为后选对,则将0.8-0.1=0.7设为参数,这样使得相似度0,8非常可能成为后选对。阈值定义的越低,行条越多,阈值越高行条越少。

3.在LSH过程中哈希桶的数目是行条数的几倍?

局部敏感哈希过程中,输入的参数的数目等于行条的数目,当桶数目为1倍时,恰好桶数目足够。当然也很有可能产生哈希冲突,如果为2倍的话,哈希冲突的可能性会降低。这时候对程序的执行效率并不会降低。因为仅仅是增加了hash函数的mod后的数值。

【单机版,以两个文件为例】K-Shingle+最小Hash签名+LSH算法+LSH族....Java代码相关推荐

  1. java算法 例 百度云_Java版数据结构与算法(20集版)视频教程百度云下载

    课程目录: 1 Y) C+ M. ~9 S' r7 i  J# _数据结构-Java版(20集)7 {2 h5 w' i9 C' }& }$ J |____第20讲 - 图的最小生成树.avi ...

  2. 可视化反编译APK工具,查看部分JAVA代码与全部资源文件。

    ###前言 APK是安卓安装包,里面主要就是两大部分 资源文件,主要是图片与布局. dex文件,也就是各种代码转换成的Android可执行文件. 如果直接ZIP打开混淆打包过的APK的话,会出现dex ...

  3. Hadoop环境下用java代码实现hdfs远程文件的上传和下载

    Hadoop环境下用java代码实现hdfs远程文件的上传和下载 文章目录 Hadoop环境下用java代码实现hdfs远程文件的上传和下载 一.新建maven工程 二.文件的上传 三.文件的下载 四 ...

  4. ffmpeg学习五:avformat_open_input函数源码分析(以mp4文件为例)

    上一节我们写了一个简单的程序,它可以把一个视频文件解码成多张图片.我们只是简单的使用的ffmepg提供的api来实现这一过程的,但对api具体的实现过程却一无所知,因此,从这篇博客看是,就逐步分析这些 ...

  5. awk数组处理两个文件的例子

    awk数组处理两个文件的例子  如果文件a中包含文件b,则将文件b的记录打印出来输出到c文件里 文件a:  10/05766798607,11/20050325191329,29/0.1,14/057 ...

  6. linux shell 删除两个文件相同部分

    目录 方法一:使用grep 方法二:实用comm 方法三:使用awk 首先描述一下这个问题:比如两个文件file1和file2,删除两个文件中共同的部分,留下两个文件中独自有的部分 方法一:使用gre ...

  7. 给定a、b两个文件,各存放50亿个url,每个url各占64字节

    给定a.b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a.b文件共同的url? 两种方法: 一.采用Bloom filter,假设布隆过滤器的错误率为0.01,则位 ...

  8. 账单比较java代码_Java代码比较两个文件的MD5

    比较MD5就可以知道两个文件是否一样,那为什么要知道文件一不一样呢? 有时候开发的时候自己本地环境跑的功能一切正常,到了测试环境却不行了,那有可能是测试环境的文件没有部署到最新的. 怎么判断是否最新呢 ...

  9. Linux如何显示文件数目的技巧比较两个文件夹是否相同

    文章目录 如何对比文件数目 比较两个文件夹是否相同 查看文件大小并排序文件大小 工作场景,现在我需要对比同事的一个目录下的文件,与我的目录下的文件数量是否相同,如果数量相同的话,文件是否相同. 如何对 ...

最新文章

  1. VS 2012 如何发布 ASP.NET 网站到本地IIS
  2. 一个sqlserver数据库表查看与备份软件
  3. 关于Android手机CPU不同架构的问题
  4. ns3入门案例1 first.cc
  5. C语言 二维数组定义和使用 - C语言零基础入门教程
  6. slice,Array.prototype.slice,Array.protyotype.slice.call
  7. sharepoint 人员搜索问题
  8. sonar覆盖率怎么统计的_实战|Java 测试覆盖率 Jacoco插桩的不同形式总结和踩坑记录(上)...
  9. python查看系统进程_在Python中获取操作系统的进程信息
  10. XBMC Skinning Manual
  11. 一位女孩对男孩的忠告(转贴)
  12. 使用软件实现直播推流
  13. 泰勒展开式推导梯度下降
  14. Python学习(二)列表,for循环,切片,元组
  15. 从经济学角度解释:为什么画家总是死后成名?
  16. 最好用的coreldraw2020中文版,安装好后不显示评估版,显示已注册
  17. 英国开发者年龄歧视为29岁,女程序员幸福指数略高于男性 | 全球开发者幸福指数报告
  18. 波特率、比特、字、千字节、兆、G之间转换
  19. Ubuntu卸载软件
  20. GMap.net 自定义Marker

热门文章

  1. matplotlib 改变时间刻度间隔 改变时间刻度格式
  2. unity实现截屏功能
  3. 基于经纬度进行商圈定位
  4. 带你从零玩转云服务器
  5. SOR迭代法python实现
  6. 绘制简单的美国疫情地图(plotlty+request)
  7. 手工编写html5,HTML5实现动画效果的方式汇总
  8. opencv:02 读取视频,显示视频,保存视频
  9. 把html转换为json数据,HTML转成JSON数据
  10. JS-隔行换色+鼠标移上去变色