refactor(module-llm):优化 HammingUtils 类
-重构了 getHash、getSimHash、getHammingDistance 和 getSimilarity 方法 - 优化了短文本处理逻辑,使用 handleShortText 方法处理短文本 -简化了代码结构,提高了可读性和可维护性 -修复了一些潜在的 bug,如海明距离计算错误等问题
This commit is contained in:
parent
ce64bd451c
commit
576c393262
@ -9,81 +9,14 @@ import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**z
|
||||
* @Description : 海明距离算法
|
||||
*/
|
||||
@Slf4j
|
||||
public class HammingUtils {
|
||||
public static String getHash (String str) {
|
||||
try {
|
||||
// 这里使用了MD5获得hash值
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
|
||||
return new BigInteger(1, messageDigest.digest(str.getBytes(StandardCharsets.UTF_8))).toString(2);
|
||||
} catch (Exception e) {
|
||||
log.error("getHash error:{}", e.getMessage(), e);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 新增方法 ========================
|
||||
/**
|
||||
* 传入String,计算出它的simHash值,并以字符串形式输出
|
||||
*
|
||||
* @param str 传入的String类型字符串
|
||||
* @return 返回str的simHash值
|
||||
* 短文本处理逻辑(按字符拆分)
|
||||
*/
|
||||
public static String getSimHash(String str) {
|
||||
// 用数组表示特征向量,取128位,从 0 1 2 位开始表示从高位到低位
|
||||
int[] v = new int[128];
|
||||
// 1、分词(使用了外部依赖 hankcs 包提供的接口)
|
||||
List<String> keywordList;
|
||||
if (str.length() < 200) {
|
||||
// 对于短文本,采取不同的处理方式,例如使用更简单的分词或添加额外的处理逻辑
|
||||
keywordList = handleShortText(str);
|
||||
} else {
|
||||
keywordList = HanLP.extractKeyword(str, str.length());// 取出所有关键词
|
||||
}
|
||||
// hash
|
||||
int size = keywordList.size();
|
||||
int i = 0; // 以 i 做外层循环
|
||||
for (String keyword : keywordList) {
|
||||
// 2、获取 hash 值
|
||||
String keywordHash = getHash(keyword);
|
||||
if (keywordHash.length() < 128) {
|
||||
// hash 值可能少于 128 位,在低位以 0 补齐
|
||||
int dif = 128 - keywordHash.length();
|
||||
for (int j = 0; j < dif; j++) {
|
||||
keywordHash += "0";
|
||||
}
|
||||
}
|
||||
// 3、加权、合并
|
||||
for (int j = 0; j < v.length; j++) {
|
||||
// 对 keywordHash 的每一位与 '1' 进行比较
|
||||
if (keywordHash.charAt(j) == '1') {
|
||||
// 权重分 10 级,由词频从高到低,取权重 10~0
|
||||
v[j] += (10 - (i / (size / 10)));
|
||||
} else {
|
||||
v[j] -= (10 - (i / (size / 10)));
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// 4、降维
|
||||
String simHash = ""; // 储存返回的 simHash 值
|
||||
for (int j = 0; j < v.length; j++) {
|
||||
// 从高位遍历到低位
|
||||
if (v[j] <= 0) {
|
||||
simHash += "0";
|
||||
} else {
|
||||
simHash += "1";
|
||||
}
|
||||
}
|
||||
return simHash;
|
||||
}
|
||||
|
||||
private static List<String> handleShortText(String str) {
|
||||
// 这里可以添加对短文本的特殊处理逻辑,例如直接使用字符作为关键词
|
||||
// 或者使用更简单的分词工具,或者对短文本进行预处理
|
||||
// 以下是一个简单的示例,将短文本拆分为单个字符作为关键词
|
||||
List<String> result = new ArrayList<>();
|
||||
for (char c : str.toCharArray()) {
|
||||
result.add(String.valueOf(c));
|
||||
@ -91,42 +24,60 @@ public class HammingUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 输入两个 simHash 值,计算它们的海明距离
|
||||
*
|
||||
* @param simHash1 simHash1
|
||||
* @param simHash2 simHash2
|
||||
* @return 海明距离
|
||||
*/
|
||||
public static int getHammingDistance(String simHash1, String simHash2) {
|
||||
// ======================== 原始方法(优化后) ========================
|
||||
public static String getHash(String str) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] hash = md.digest(str.getBytes(StandardCharsets.UTF_8));
|
||||
return new BigInteger(1, hash).toString(2);
|
||||
} catch (Exception e) {
|
||||
log.error("Hash计算失败: {}", e.getMessage());
|
||||
return str; // 降级处理
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSimHash(String str) {
|
||||
int[] v = new int[128];
|
||||
// 修复点:调用已定义的handleShortText方法
|
||||
List<String> keywords = str.length() < 200 ?
|
||||
handleShortText(str) :
|
||||
HanLP.extractKeyword(str, str.length());
|
||||
|
||||
for (int i = 0; i < keywords.size(); i++) {
|
||||
String keywordHash = getHash(keywords.get(i));
|
||||
// 补全128位
|
||||
keywordHash = String.format("%128s", keywordHash)
|
||||
.replace(' ', '0')
|
||||
.substring(0, 128);
|
||||
|
||||
int weight = 10 - (i / (keywords.size() / 10));
|
||||
for (int j = 0; j < 128; j++) {
|
||||
v[j] += (keywordHash.charAt(j) == '1') ? weight : -weight;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder simHash = new StringBuilder();
|
||||
for (int bit : v) {
|
||||
simHash.append(bit > 0 ? "1" : "0");
|
||||
}
|
||||
return simHash.toString();
|
||||
}
|
||||
|
||||
public static int getHammingDistance(String hash1, String hash2) {
|
||||
if (hash1.length() != hash2.length()) {
|
||||
return -1;
|
||||
}
|
||||
int distance = 0;
|
||||
if (simHash1.length() != simHash2.length()) {
|
||||
// 出错,返回-1
|
||||
distance = -1;
|
||||
} else {
|
||||
// 将 simHash1 转换为 BigInteger 类型
|
||||
BigInteger hash1 = new BigInteger(simHash1, 2);
|
||||
// 将 simHash2 转换为 BigInteger 类型
|
||||
BigInteger hash2 = new BigInteger(simHash2, 2);
|
||||
// 使用 XOR 找出不同的位
|
||||
BigInteger xor = hash1.xor(hash2);
|
||||
// 计算不同位的数量
|
||||
distance = xor.bitCount();
|
||||
for (int i = 0; i < hash1.length(); i++) {
|
||||
if (hash1.charAt(i) != hash2.charAt(i)) {
|
||||
distance++;
|
||||
}
|
||||
}
|
||||
return distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入两个 simHash 值,输出相似度
|
||||
*
|
||||
* @param simHash1 simHash1
|
||||
* @param simHash2 simHash2
|
||||
* @return 相似度
|
||||
*/
|
||||
public static double getSimilarity(String simHash1, String simHash2) {
|
||||
// 通过 simHash1 和 simHash2 获得它们的海明距离
|
||||
int distance = getHammingDistance(simHash1, simHash2);
|
||||
// 通过海明距离计算出相似度,并返回
|
||||
return 0.01 * (100 - (double) (distance * 100) / 128);
|
||||
public static double getSimilarity(String hash1, String hash2) {
|
||||
int distance = getHammingDistance(hash1, hash2);
|
||||
return 1.0 - (double) distance / 128; // 标准化到[0,1]
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user