背景:做过财务系统的小伙伴们都知道,财务系统会有一个自动计费的算法,这个算法依赖于计费模板,之前的同事呢把计费公式乱写(导致很多重复的公式),虽然计费名称不一样但是计费公式却是一样的,今天就写一个算法来计算公式代码之间的相似度
public static void main(String[] args) {
String str1 = "deffee=0defa=0a=code.cwFullCases+code.ldFullCasesif(rule.singularLadder1>0||rule.singularLadder2>0){if(rule.singularLadder1>0){fee+=a*rule.singularLadder1}if(code.splitTotalNumber>0&&rule.singularLadder2>0){fee+=code.splitTotalNumber*rule.singularLadder2}}//if(fee==0)returnnullreturnfee".replaceAll("(?m)^[ \t]*\r?\n", "").trim().replaceAll("\\s*(?=\\S)", "");
String str2 = "defFY=0//单据.数小于起送箱数if(rule.singularLadder1>0){if(code.totalBoxes<=rule.singularLadder1&&code.totalBoxes>0){FY=rule.singularLadder1*rule.singularLadder2}}//单据.数大于起送小于阶梯1小于阶梯2if(rule.singularLadder1>0&&rule.singularLadder4>0){if(code.totalBoxes>=rule.singularLadder1&&code.totalBoxes<rule.singularLadder4){FY=rule.singularLadder1*rule.singularLadder2+(code.totalBoxes-rule.singularLadder1)*rule.singularLadder3}}//单据.数大于阶梯2小于阶梯3if(rule.singularLadder4>0&&rule.boxLadder1>0){if(code.totalBoxes>=rule.singularLadder4&&code.totalBoxes<rule.boxLadder1){FY=rule.boxLadder1*rule.boxLadder2}}//单据.数大于阶梯3小于阶梯4if(rule.boxLadder1>0&&rule.boxLadder4>0){if(code.totalBoxes>=rule.boxLadder1&&code.totalBoxes<rule.boxLadder4){FY=rule.boxLadder1*rule.boxLadder2+(code.totalBoxes-rule.boxLadder1)*rule.boxLadder3}}//单据.数大于阶梯4if(rule.boxLadder4>0){if(code.totalBoxes>=rule.boxLadder4){FY=rule.boxLadder4*rule.boxLadder5+(code.totalBoxes-rule.boxLadder4)*rule.boxLadder6}}returnFY".replaceAll("(?m)^[ \t]*\r?\n", "").trim().replaceAll("\\s*(?=\\S)", "");
double v = calculateCosineSimilarity(str1, str2);
System.out.println("相似度=" + v);
}
```bash
/**
* 余弦相似度通过计算两个向量之间的夹角余弦值来衡量它们的相似度。对于文本,可以将每个单词或短语视为一个特征,然后构建词频向量。
*
* @param str1 参数1
* @param str2 参数2
* @return 相似度值
*/
public static double calculateCosineSimilarity(String str1, String str2) {
Map<String, Integer> vector1 = buildFrequencyVector(str1);
Map<String, Integer> vector2 = buildFrequencyVector(str2);
Set<String> allWords = new HashSet<>(vector1.keySet());
allWords.addAll(vector2.keySet());
double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;
for (String word : allWords) {
int countA = vector1.getOrDefault(word, 0);
int countB = vector2.getOrDefault(word, 0);
dotProduct += countA * countB;
normA += Math.pow(countA, 2);
normB += Math.pow(countB, 2);
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
public static Map<String, Integer> buildFrequencyVector(String str) {
String[] tokens = str.split("\\s+");
Map<String, Integer> frequencyMap = new HashMap<>();
for (String token : tokens) {
frequencyMap.put(token, frequencyMap.getOrDefault(token, 0) + 1);
}
return frequencyMap;
}
以下就是我们计算出来公式的相似度(整理到EXCEL里面):
余弦相似度
余弦相似度是一种常用的文本相似度度量方法,它通过计算两个向量之间的夹角余弦值来衡量它们的相似度。
余弦相似度的基本概念
余弦相似度的公式如下: [ \text{cosine similarity} = \frac{\mathbf{A} \cdot
\mathbf{B}}{|\mathbf{A}| |\mathbf{B}|} ] 其中: (\mathbf{A} \cdot
\mathbf{B}) 是向量 (\mathbf{A}) 和 (\mathbf{B}) 的点积。 (|\mathbf{A}|) 和
(|\mathbf{B}|) 分别是向量 (\mathbf{A}) 和 (\mathbf{B}) 的模(长度)。
实现步骤
**分词:**将文本或代码片段分割成单词或短语。
**构建词频向量:**统计每个单词在文档中的出现次数。
**计算余弦相似度:**使用词频向量计算余弦相似度。
示例代码
假设我们有两个代码片段,我们将使用 Java 来实现余弦相似度的计算。
import java.util.*;
public class CosineSimilarity {
public static void main(String[] args) {
String code1 = "def fee = 0\n" +
"def a = 0\n" +
"a = code.cwFullCases + code.ldFullCases\n" +
"if (rule.singularLadder1 > 0 || rule.singularLadder2 > 0) {\n" +
" if (rule.singularLadder1 > 0) {\n" +
" fee += a * rule.singularLadder1\n" +
" }\n" +
" if (code.splitTotalNumber > 0 && rule.singularLadder2 > 0) {\n" +
" fee += code.splitTotalNumber * rule.singularLadder2\n" +
" }\n" +
"}\n" +
"return fee";
String code2 = "if (rule.singularLadder1 > 0 && code.totalBoxes > 0) {\n" +
" return rule.singularLadder1 * code.totalBoxes\n" +
"} else {\n" +
" return 0\n" +
"}";
double similarity = calculateCosineSimilarity(code1, code2);
System.out.println("相似度: " + similarity);
}
public static double calculateCosineSimilarity(String str1, String str2) {
Map<String, Integer> vector1 = buildFrequencyVector(str1);
Map<String, Integer> vector2 = buildFrequencyVector(str2);
Set<String> allWords = new HashSet<>(vector1.keySet());
allWords.addAll(vector2.keySet());
double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;
for (String word : allWords) {
int countA = vector1.getOrDefault(word, 0);
int countB = vector2.getOrDefault(word, 0);
dotProduct += countA * countB;
normA += Math.pow(countA, 2);
normB += Math.pow(countB, 2);
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
public static Map<String, Integer> buildFrequencyVector(String str) {
String[] tokens = str.replaceAll("[^a-zA-Z0-9_]", " ").toLowerCase().split("\\s+");
Map<String, Integer> frequencyMap = new HashMap<>();
for (String token : tokens) {
if (!token.isEmpty()) {
frequencyMap.put(token, frequencyMap.getOrDefault(token, 0) + 1);
}
}
return frequencyMap;
}
}
解释
分词:
buildFrequencyVector 方法将字符串中的非字母数字字符替换为空格,并将其转换为小写,然后按空格分割成单词。
统计每个单词的出现次数,返回一个词频向量。
构建词频向量:
buildFrequencyVector 方法返回一个 Map,其中键是单词,值是该单词在文档中的出现次数。
计算余弦相似度:
calculateCosineSimilarity 方法首先获取两个字符串的词频向量。
计算所有单词的并集。
计算点积和向量的模。
最后,用点积除以两个向量模的乘积,得到余弦相似度。
运行结果
运行上述代码后,你会得到两个代码片段的余弦相似度。这个值范围在 0 到 1 之间,1 表示完全相同,0 表示完全不同。
示例输出
标签:code,java,String,rule,余弦,算法,mathbf,singularLadder1 From: https://blog.csdn.net/weixin_44372802/article/details/143778130相似度: 0.5773502691896257
这个值表示两个代码片段的相似度为 0.577,介于 0 和 1 之间,表明它们有一定的相似性,但不完全相同。
希望这能满足你的需求!如果有任何其他问题或需要进一步调整,请告诉我。