Scala实现精简版SimHash计算

前面一篇文章介绍了SimHash算法的基本原理和主要的应用场景。由于SimHash算法是一种在大规模数据下解决文本相似与否的算法,这篇文章主要介绍一下自己基于Scala实现的一种精简版SimHash算法。

算法思路:

对于一个文本内容,先使用正则表达式对无意义字符进行过滤,再以滑动切片的方法将文本切成一个字符串数组,其中滑动切片窗口的阈值视情况而定。然后对该字符串数组,进行权重派分。代码中给出了前缀、后缀和平摊的方案进行派分,权重赋值为1和3(详细请看代码)。然后通过哈希函数对每个分片字符串进行计算,并生成该分片的权重数组。接下来把所有分片权重数组累加,再将64位权重数组大于0的位置1,其余置0,生成最终的SimHash值。

哈希值计算,由于生成64位哈希值,为了方便后面操作,使用位数组。

  def byte2Bools(b: Byte) =
    (0 to 7).foldLeft(ArrayBuffer[Boolean]())((bs, i) => bs += isBitSet(b, i)).reverse

  def isBitSet(byte: Byte, bit: Int) =
    ((byte >> bit) & 1) == 1

  def hashFuc(st:String):ArrayBuffer[Boolean] ={
    var str
    =org.apache.commons.codec.digest.DigestUtils.md5Hex(st)
    var hashV = new java.math.BigInteger(str,16)
    var hashArrByte =hashV.toByteArray()
    var hashArrBoolean = ArrayBuffer[Boolean]()
    var len = hashArrByte.length
    for(i<- 0 to len-1){
      var temArr = byte2Bools(hashArrByte(i))
      hashArrBoolean++=temArr
    }
    hashArrBoolean
  }

正则过滤函数,用来给文本去掉意义不大的字符。

  def tokenlize(str:String):ArrayBuffer[String]={
    var reg = """[\w\u4e00-\u9fcc]+""".r
    var s = str.toLowerCase()
    var reStr = (reg findAllIn s).mkString("")
    var ans = slide(reStr)
    ans
  }

滑动切片函数,用来将文本切片。

  def slide(str:String): ArrayBuffer[String] ={
    val slideWidth = 4
    var len = str.length
    var res = ArrayBuffer[String]()
    var a = 0
    if(len>=4){
      for(a <- 0 to (len - slideWidth)){
        var s = str.slice(a,a+slideWidth)
        res+=s
      }
    }else{
      res+= str
    }
    res
  }

对切片数组赋予权重

  def weightCul(Type:String,length:Int):ArrayBuffer[Int] = {
    var weights = ArrayBuffer[Int]()
    val len = length/2 + 1
    val a = 1
    if(weightType=="pre"){
      for(a<- 1 to len){
        weights+= 3
      }
      for(a<- 1 to length-len){
        weights+= 1
      }

    }else{
      if(weightType=="suf"){
        for (a<- 1 to length-len){
          weights+= 1
        }
        for (a<- 1 to len){
          weights+= 3
        }

      }else{
        for (a<- 1 to length){
          weights+= 1
        }
      }
    }
    weights
  }

对文本进行SimHash值计算

  def buildByArr(fea:ArrayBuffer[(String, Int)]):String = {
    var v = ArrayBuffer[Int]()
    val f = 64
    for (i<- 1 to f){
      v+= 0
    }
    var len=fea.length
    for (j<- 0 to len-1){
      var feature = fea(j)
      var hashArr = hashFuc(feature._1)
      var weight = feature._2
      var hLen = hashArr.length
      for(a<-0 to f-1){
        if ((hashArr(hLen-1-a))){
          v(a)+= weight
        }else{
          v(a)-= weight
        }
      }
    }
    var list = new java.math.BigInteger("0")
    for(k<- 0 to f-1){
      if(v(k)>0){
        list = list.setBit(k)
      }
    }
    val simHash = list.toString
    SimHash
  }

至此,就可以计算出在不同权重方案下SimHash的值。

这种计算方法比较适合较短文本的计算,长文本采用分词的方式较为有效。

猜你喜欢

转载自blog.csdn.net/Daverain/article/details/80952403
今日推荐