• 关于SimHash算法的实现及测试V4.0


    @祁俊辉,2017年6月15日测试。

    1  说明

    • 本程序衔接关于SimHash算法的实现及测试V3.0
    • 改进1:增加TF-IDF算法,用于计算词权重(本地新增100篇txt文本库);
    • 改进2:各个程序衔接,详情见流程图。

    2  程序

    目前项目中存在4个类,分别是分词“FenCi”,计算某个词在多少个文档中出现过“TxtComparison”,计算TF-IDF值“TF_IDF”,计算SimHash值及相似度比较“SimHash128Txt”。

    2.1  FenCi

     1 /* 【函数作用】对指定txt文档分词,存入另一文档中
     2  * 【说明】1.该程序采用IK分词包(前向)进行分词
     3  * 【时间】祁俊辉->2017.6.9
     4  * */
     5 import java.io.*;
     6 
     7 import org.apache.lucene.analysis.Analyzer;
     8 import org.apache.lucene.analysis.TokenStream;
     9 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
    10 import org.wltea.analyzer.lucene.IKAnalyzer;
    11 
    12 public class FenCi {
    13     static String txt_ys = "./SimHash文档库/原始文档9.txt";
    14     static String txt_fch = "./SimHash文档库/分词后文档9.txt";
    15     static String txt_zh = "./SimHash文档库/最终文档9.txt";
    16     
    17     //输入字符串,输出分词后的字符串
    18     public static String Seg(String sentence) throws IOException {
    19         String text="";
    20         //创建分词对象
    21         Analyzer anal=new IKAnalyzer(true);
    22         StringReader reader=new StringReader(sentence);
    23         //分词
    24         TokenStream ts=anal.tokenStream("", reader);
    25         CharTermAttribute term=ts.getAttribute(CharTermAttribute.class);
    26         //遍历分词数据
    27         while(ts.incrementToken()){
    28             text+=term.toString()+"/";
    29         }
    30         reader.close();
    31         anal.close();
    32         return text.trim()+"
    ";
    33     }
    34 
    35     public static void main(String[] args) {
    36         File file_ys = new File(FenCi.txt_ys);//原始文件
    37         File file_fch = new File(FenCi.txt_fch);//分词后文件
    38         try {
    39             FileReader fr = new FileReader(file_ys);
    40             BufferedReader bufr = new BufferedReader(fr);
    41             FileWriter fw = new FileWriter(file_fch);
    42             BufferedWriter bufw = new BufferedWriter(fw);
    43             String s = null;
    44             int i = 0;
    45             while((s = bufr.readLine()) != null) {
    46                 i++;
    47                 String s_fch = FenCi.Seg(s);
    48                 System.out.println("第"+i+"行的原始数据:");
    49                 System.out.println(s);
    50                 System.out.println("第"+i+"行分词后的结果:");
    51                 System.out.println(s_fch);
    52                 bufw.write(s_fch);
    53                 bufw.newLine();
    54             }
    55             bufr.close();
    56             fr.close();
    57             bufw.close();
    58             fw.close();
    59             System.out.println("文件处理完毕,已生成该文档的分词后文档!");
    60         } catch (Exception e) {
    61             e.printStackTrace();
    62         }
    63     }
    64 }

    2.2  TxtComparison

     1 /* 【函数作用】判断str字符串在多少个文档中出现过
     2  * 【说明】1.目前文件库有100篇文档,查找总耗时小于1秒
     3  * 【说明】2.调用时,用"TxtComparison.Txt(str)",返回值为(出现次数+1)
     4  * 【说明】3.之所以加1,是因为如果该词在文件库没有出现,则计算IDF值时会出错
     5  * 【时间】祁俊辉->2017.6.14
     6  * */
     7 import java.io.*;
     8 
     9 public class TxtComparison {
    10     public static int Txt(String str) {
    11         int text_num = 0;//定义总出现次数
    12         for(int i=0; i<100; i++){//遍历100篇文档
    13             String filename = "./SimHash文档库/文件库/参考文档";
    14             filename += (i+".txt");//组合定义文件路径及文件名
    15             File file_name = new File(filename);//创建文件路径
    16             try {
    17                 InputStreamReader isr = new InputStreamReader(new FileInputStream(file_name),"UTF-8");
    18                 //编码方式UTF-8,防止中文乱码
    19                 BufferedReader bufr = new BufferedReader(isr);
    20                 String s = null;//临时存储每行数据
    21                 boolean num = false;//临时存储目标文档是否含有指定字符串
    22                 while((s = bufr.readLine()) != null) {//遍历目标文档每行
    23                     //System.out.println(s);
    24                     if(s.contains(str)){//如果含有指定字符串
    25                         //System.out.println("第" + i + "篇文章中有");
    26                         num = true;//标志位赋值
    27                         break;//退出(节省时间)
    28                     }
    29                 }
    30                 bufr.close();
    31                 isr.close();
    32                 if(num) {//根据标志位状态,让总出现次数增加
    33                     text_num ++;
    34                 }
    35             } catch (Exception e) {
    36                 e.printStackTrace();
    37             }
    38         }
    39         return text_num+1;//返回总出现次数+1
    40     }
    41 }

    2.3  TF_IDF

     1 /* 【函数作用】计算分词后的文档各词的IF-IDF值
     2  * 【说明】1.目前文件库有100篇文档,查找总耗时小于1秒
     3  * 【说明】2.调用时,用"TxtComparison.Txt(str)",返回值为(出现次数+1)
     4  * 【说明】3.之所以加1,是因为如果该词在文件库没有出现,则计算IDF值时会出错
     5  * 【时间】祁俊辉->2017.6.14
     6  * */
     7 import java.io.BufferedWriter;
     8 import java.io.File;
     9 import java.io.FileWriter;
    10 import java.io.IOException;
    11 
    12 public class TF_IDF {
    13     
    14     public static void main(String[] args) throws IOException {
    15         //生成的最终文件
    16         File file_zh = new File(FenCi.txt_zh);//最终文件
    17         FileWriter fw = new FileWriter(file_zh);
    18         BufferedWriter bufw = new BufferedWriter(fw);
    19         //对分词后的文档进行处理
    20         String s = SimHash128Txt.Txt_read(FenCi.txt_fch);
    21         String[] WordArray = s.split("/");
    22         
    23         for(int i=0; i<WordArray.length; i++){//遍历每个词
    24             /*第一步:计算TF值*/
    25             int TF_Fre = 0;//定义频率为0
    26             //计算每个词的频率
    27             for(int j=0; j<WordArray.length; j++){
    28                 if(WordArray[i].equals(WordArray[j])){
    29                     TF_Fre++;
    30                 }
    31             }
    32             //频率归一化
    33             float TF = (float) (1.0*TF_Fre/WordArray.length);
    34             //System.out.println(WordArray[i]+"的TF值为"+TF);
    35             /*第二步:计算IDF值*/
    36             //计算每个词在文件库出现的文件数
    37             int IDF_a = TxtComparison.Txt(WordArray[i]);
    38             //System.out.println(WordArray[i]+"在文件库中出现了"+IDF_a);
    39             //总文件数  除以  该词在文件库中出现的次数
    40             float IDF_b = (float) (101.0/IDF_a);
    41             //System.out.println(IDF_b);
    42             //IDF = log 2 (IDF_b)
    43             float IDF = (float) (Math.log(IDF_b)/Math.log(2));
    44             //System.out.println(WordArray[i]+"的IDF值为"+IDF);
    45             /*第三步:计算TF-IDF值*/
    46             float TFIDF = TF * IDF;
    47             System.out.println(WordArray[i]+"的TF-IDF值为"+TFIDF);
    48             /*第四步:将计算结果写入txt文件*/
    49             bufw.write(WordArray[i] + "##" + TFIDF + "/");
    50             bufw.newLine();
    51         }
    52         bufw.close();
    53         fw.close();
    54         System.out.println("文件处理完毕,已生成该文档的最终文档!");
    55     }
    56 }

    2.4  SimHash128Txt

      1 /* 【算法】SimHash->128位
      2  * 【说明】1.加上权重(TF-IDF值)
      3  * 【说明】2.经IK分词后,将分词后的文件存储,该程序进行读取
      4  * 【时间】祁俊辉->2017.6.15
      5  * */
      6 
      7 import java.io.*;
      8 import java.math.BigInteger;
      9 import java.security.MessageDigest;
     10 
     11 public class SimHash128Txt {
     12     //以下两个文档为新增文档,两段话,检测相似度
     13     static String s9 = SimHash128Txt.Txt_read("./SimHash文档库/最终文档9.txt");
     14     static String s10 = SimHash128Txt.Txt_read("./SimHash文档库/最终文档10.txt");
     15     /* 函数名:Txt_read(String name)
     16      * 功能:读取name文件的内容,name为txt文件名
     17      * */
     18     static String Txt_read(String name){
     19         String s = "";
     20         File file = new File(name);//原始文件
     21         try {
     22             FileReader fr = new FileReader(file);
     23             BufferedReader bufr = new BufferedReader(fr);
     24             String s_x = null;
     25             //int i = 0;
     26             while((s_x = bufr.readLine()) != null) {
     27                 //i++;
     28                 //System.out.println("第"+i+"行的原始数据:");
     29                 s += s_x;
     30             }
     31             bufr.close();
     32             fr.close();
     33         } catch (Exception e) {
     34             e.printStackTrace();
     35         }
     36         return s;
     37     }
     38     /* 函数名:MD5_Hash(String str)
     39      * 功能:计算字符串str的128位hash值,并将其以String型返回
     40      * */
     41     static String MD5_Hash(String str){
     42         try{
     43             // 生成一个MD5加密计算摘要
     44             MessageDigest md = MessageDigest.getInstance("MD5");
     45             // 计算md5函数
     46             //System.out.println("字符串:"+str);
     47             //System.out.println("字符串的MD5_Hash:"+md.digest(str.getBytes()));
     48             // digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
     49             // BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
     50             return new BigInteger(1,md.digest(str.getBytes("UTF-8"))).toString(2);
     51         }catch(Exception e){
     52             e.printStackTrace();
     53             return str;
     54         }
     55     }
     56     /* 函数名:First_FC(String str)
     57      * 功能:1.先创建一个存储SimHash值的128数组,并初始化为0
     58      * 功能:2.将str字符串分词,并存入临时数组
     59      * 功能:3.计算此字符串的SimHash值(加权、但没有降维),存储在数组中
     60      * 功能:4.将数组中的SimHash值降维,并以字符串形式返回
     61      * */
     62     static String First_FC(String str){
     63         //1.先创建一个存储SimHash值的128数组,并初始化为0
     64         float Hash_SZ[] = new float[128];
     65         for(int i=0;i<Hash_SZ.length;i++)
     66             Hash_SZ[i]=0;
     67         //2.将str字符串分词,并将词和权重存入临时数组
     68         String[] word_and_tfidf = str.split("/");//先分割
     69         String[] word = new String[word_and_tfidf.length];//创建一个词数组,大小为词个数
     70         String[] tfidf = new String[word_and_tfidf.length];//创建一个权重数组,大小为词个数
     71         //分割词,存入数组
     72         for(int i=0; i<word_and_tfidf.length; i++){
     73             String[] linshi = word_and_tfidf[i].split("##");
     74             word[i] = linshi[0];
     75             tfidf[i] = linshi[1];
     76             //System.out.println(word[i]+" "+tfidf[i]);
     77         }
     78         //3.计算此字符串的SimHash值(加权、但没有降维),存储在数组中
     79         for(int i=0;i<word.length;i++){//循环传入字符串的每个词
     80             String str_hash = SimHash128Txt.MD5_Hash(word[i]);//先计算每一个词的Hash值(128位)
     81             //MD5哈希计算时,二进制转换若最高位为7以下,也就是转换成二进制最高位为0,会不存储该0,导致后面程序出错
     82             //这里主要是为了保证它是128位的二进制
     83             if(str_hash.length() < 128){
     84                 int que = 128 - str_hash.length();
     85                 for(int j=0;j<que;j++){
     86                     str_hash = "0" + str_hash;
     87                 }
     88             }
     89             //System.out.println(str_hash);//输出该词的128位MD5哈希值
     90             char str_hash_fb[]=str_hash.toCharArray();//将该词的哈希值转为数组,方便检查
     91             //对每个词的Hash值(128位)求j位是1还是0,1的话加上该词的权重,0的话减去该词的权重
     92             for(int j=0;j<Hash_SZ.length;j++){
     93                 if(str_hash_fb[j] == '1'){
     94                     Hash_SZ[j] += Float.parseFloat(tfidf[i]);//Hash_SZ中,0是最高位,依次排低
     95                 }else{
     96                     Hash_SZ[j] -= Float.parseFloat(tfidf[i]);
     97                 }
     98             }
     99         }
    100         //4.将数组中的SimHash值降维,并以字符串形式返回
    101         String SimHash_number="";//存储SimHash值
    102         for(int i=0;i<Hash_SZ.length;i++){//从高位到低位
    103             System.out.print(Hash_SZ[i]+" ");//输出未降维的串
    104             if(Hash_SZ[i]<=0)//小于等于0,就取0
    105                 SimHash_number += "0";
    106             else//大于0,就取1
    107                 SimHash_number += "1";
    108         }
    109         System.out.println("");//换行
    110         return SimHash_number;
    111     }
    112     /* 函数名:HMJL(String a,String b)
    113      * 功能:a、b都是以String存储的二进制数,计算他们的海明距离,并将其返回
    114      * */
    115     static int HMJL(String a,String b){
    116         char[] FW1 = a.toCharArray();//将a每一位都存入数组中
    117         char[] FW2 = b.toCharArray();//将b每一位都存入数组中
    118         int haiming=0;
    119         if(FW1.length == FW2.length){//确保a和b的位数是相同的
    120             for(int i=0;i<FW1.length;i++){
    121                 if(FW1[i] != FW2[i])//如果该位不同,海明距离加1
    122                     haiming++;
    123             }
    124         }
    125         return haiming;
    126     }
    127     
    128     public static void main(String[] args) {
    129         String a9 = SimHash128Txt.First_FC(s9);
    130         String a10 = SimHash128Txt.First_FC(s10);
    131         System.out.println("【s9】的SimHash值为:"+a9);
    132         System.out.println("【s10】的SimHash值为:"+a10);
    133         System.out.println("【s9】和【s10】的海明距离为:" + SimHash128Txt.HMJL(a9,a10) + ",相似度为:" + (100-SimHash128Txt.HMJL(a9,a10)*100/128)+"%");
    134     }
    135 }

    3  测试结果

    3.1  文件说明

    100篇文档(现代小说):

    生成的文档(1篇待比较文本有3个文档:原始、分词后、最终):

    3.2  输出结果

  • 相关阅读:
    python import模块的搜索路径
    【转载】PDB命令行调试Python代码
    python 操作hdfs
    hadoop基本命令
    配置hadoop集群
    hadoop配置
    pycharm 配置spark
    pip 使用镜像下载第三方包
    pyechart.Geo -- 基于中国地图数据显示
    cv2 读取图片及展示
  • 原文地址:https://www.cnblogs.com/qijunhui/p/8448869.html
Copyright © 2020-2023  润新知