import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
public class CalculatorUtils {
private final static Logger logger = LoggerFactory.getLogger(CalculatorUtils.class);
/**
* 计算
*
* @param strA 字符串1
* @param strB 字符串2
* @return 相似度
*/
@Deprecated
public static double similarDegreeByLongestCommon(String strA, String strB) {
String newStrA = filterString(strA);
String newStrB = filterString(strB);
// 用较大的字符串长度作为分母,相似子串作为分子计算出字串相似度
int temp = Math.max(newStrA.length(), newStrB.length());
if (temp == 0) {
return 0;
}
int temp2 = longestCommonSubstring(newStrA, newStrB).length();
return temp2 * 1.0 / temp;
}
/**
* 过滤非中文和数字
*
* @param str 字符串
* @return 过滤后的数据
*/
private static String filterString(String str) {
if (StringUtils.isBlank(str)) {
return "";
}
StringBuffer sb = new StringBuffer();
for (char item : str.toCharArray()) {
if (!charRegex(item)) {
continue;
}
sb.append(item);
}
return sb.toString();
}
/**
* 判断字符是否为汉字,数字和字母(因为对符号进行相似度比较没有实际意义,故符号不加入考虑范围。)
*
* @param charValue 字符
* @return 是否中文或者数组或者英文
*/
private static boolean charRegex(char charValue) {
return (charValue >= 0x4E00 && charValue <= 0X9FA5) || (charValue >= 'a' && charValue <= 'z')
|| (charValue >= 'A' && charValue <= 'Z') || (charValue >= '0' && charValue <= '9');
}
/**
* 判断字符是否为字母
*
* @param charValue 字符
* @return 是否字母
*/
public static boolean charCharacter(char charValue) {
return (charValue >= 'a' && charValue <= 'z') || (charValue >= 'A' && charValue <= 'Z');
}
/**
* 求公共子串,采用动态规划算法。 其不要求所求得的字符在所给的字符串中是连续的。
*
* @param strA 字符串1
* @param strB 字符串2
* @return 公共子串
*/
public static String longestCommonSubstring(String strA, String strB) {
if (StringUtils.isBlank(strA) || StringUtils.isBlank(strB)) {
return "";
}
char[] charsStrA = strA.toCharArray();
char[] charsStrB = strB.toCharArray();
int m = charsStrA.length;
int n = charsStrB.length;
/*
* 初始化矩阵数据,matrix[0][0]的值为0,
* 如果字符数组charsStrA和charsStrB的对应位相同,则matrix[i][j]的值为左上角的值加1,
* 否则,matrix[i][j]的值等于左上方最近两个位置的较大值,
* 矩阵中其余各点的值为0.
*/
int[][] matrix = new int[m + 1][n + 1];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (charsStrA[i - 1] == charsStrB[j - 1]) {
matrix[i][j] = matrix[i - 1][j - 1] + 1;
} else {
matrix[i][j] = Math.max(matrix[i][j - 1], matrix[i - 1][j]);
}
}
}
/*
* 矩阵中,如果matrix[m][n]的值不等于matrix[m-1][n]的值也不等于matrix[m][n-1]的值,
* 则matrix[m][n]对应的字符为相似字符元,并将其存入result数组中。
*/
char[] result = new char[matrix[m][n]];
int currentIndex = result.length - 1;
while (matrix[m][n] != 0) {
if (matrix[m][n] == matrix[m][n - 1]) {
n--;
} else if (matrix[m][n] == matrix[m - 1][n]) {
m--;
} else {
result[currentIndex] = charsStrA[m - 1];
currentIndex--;
n--;
m--;
}
}
return new String(result);
}
/**
* {similarDegreeByLongestCommon}
* 先取两个字符串长度的最大值 maxLen,用 1-(需要操作数 除 maxLen),得到相似度。
* 短距
* https://www.cnblogs.com/xiaoyulong/p/8846745.html
* https://blog.youkuaiyun.com/u013035314/article/details/50340443
*
* @param strA 字符串1
* @param strB 字符串2
* @return Levenshtein Distance
*/
public static double getStringDistance(String strA, String strB) {
// 定义距离表
double[][] distance;
int s1Len = strA.length();
int s2Len = strB.length();
if (s1Len == 0) {
return s2Len;
}
if (s2Len == 0) {
return s1Len;
}
distance = new double[s1Len + 1][s2Len + 1];
// 二维数组第一行和第一列放置自然数
for (int i = 0; i <= s1Len; i++) {
distance[i][0] = i;
}
for (int j = 0; j <= s2Len; j++) {
distance[0][j] = j;
}
// 比较,若行列相同,则代价为0,否则代价为1;
for (int i = 1; i <= s1Len; i++) {
char s1I = strA.charAt(i - 1);
// 逐一比较
for (int j = 1; j <= s2Len; j++) {
char s2J = strB.charAt(j - 1);
// 若相等,则代价取0;直接取左上方值
if (s1I == s2J) {
distance[i][j] = distance[i - 1][j - 1];
} else {
// 否则代价取1,取左上角、左、上 最小值 + 代价(代价之和便是最终距离)
distance[i][j] = getMin(distance[i - 1][j], distance[i][j - 1], distance[i - 1][j - 1]) + 1;
}
}
}
// 取二位数组最后一位便是两个字符串之间的距离
return distance[s1Len][s2Len];
}
/**
* 取最小值
*
* @param a 数值1
* @param b 数值2
* @param c 数值3
* @return 最小值
*/
private static double getMin(double a, double b, double c) {
double min = a;
if (b < min) {
min = b;
}
if (c < min) {
min = c;
}
return min;
}
/**
* 效率高点
* 计算
*
* @param strA 字符串1
* @param strB 字符串2
* @return 相似度
*/
public static double similarDegreeByDistance(String strA, String strB) {
String newStrA = filterString(strA);
String newStrB = filterString(strB);
// 用较大的字符串长度作为分母,相似子串作为分子计算出字串相似度
int temp = Math.max(newStrA.length(), newStrB.length());
if (temp == 0) {
return 0;
}
double temp2 = getStringDistance(newStrA, newStrB);
return 1 - (temp2 / temp);
}
/**
* 除法计算 (保留四位小数,四舍五入)
* @param current
* @param pre
* @param <T>
* @return
*/
public static <T extends Number> BigDecimal countDivision(T current, T pre) {
BigDecimal result = null;
if (Objects.isNull(current) || Objects.isNull(pre)) {
return result;
}
BigDecimal currentNum = new BigDecimal(current.toString());
BigDecimal preNum = new BigDecimal(pre.toString());
if (preNum.compareTo(BigDecimal.ZERO) == 0) {
return null;
}
// 保留四位小数
result = currentNum.divide(preNum.abs(),4, RoundingMode.HALF_UP);
return result;
}
}