把OA²+OB²-AB² / 20A0B 公式改为坐标特征来计算
根据勾股定理
OA²= X11² + X12²
OB²=X21² + X22²
AB²=(X12-X22)² + (X21-X11)²
整式为
X11² + X12²+ X21² + X22²-[(X12-X22)² + (X21-X11)²] / 2* 根号下 X11² + X12² * 根号下 X21² + X22²
X12*X22 + X21*X11 / 根号下 X11² + X12² * 根号下 X21² + X22²
X12 * X22 + X21 * X11 + X13* X23 / 根号下 X11² + X12²+ X13² * 根号下 X21² + X22² + X23²
根据我们常人更改密码的习惯,我们很好会有将密码改的和之前相比面目全非的,因为这样的话会严重增加了我们的记忆成本,在现在网络时代各种账号密码存在的情况,很容易造成记错和忘记的情况,所以我们修改密码通常都是在之前密码上做增添或修改,以便于记忆和回想!
思路整理:
所以我们将所有的历史密码所用到的字节全部收集进行排序去重,作为字袋!
然后那密码和字袋进行如图比对 ,形成向量坐标的存在,
然后将输入的密码向量和历史密码向量利用前面我们推导出来的余弦相似来算出相似度!
进行密码相似度评估
package com.baizhi.evaluate.impl;
import com.baizhi.enties.EvaluateData;
import com.baizhi.enties.EvaluateReport;
import com.baizhi.enties.HistoryData;
import com.baizhi.enties.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import com.sun.java.swing.plaf.windows.WindowsTextAreaUI;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.Arrays.stream;
public class SimilarityEvaluate extends Evaluate {
private Double threshold; //阈值
public SimilarityEvaluate(Double threshold) {
super(RiskFactor.SIMILARITY);
this.threshold = threshold;
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport,
EvaluateChain evaluateChain) {
evaluateReport.signReport(getRiskFactor()
,doSimilarity(evaluateData.getOrdernessPassword(),historyData.getHistoryOrdernessPasswords()));
//往下传
evaluateChain.daChain(evaluateData,historyData,evaluateReport);
}
/**
* 思路
* ① 先获取出字袋
* ② 算出历史密码的特征向量
* ③ 算出本次登录密码的特征向量
* ⑤ 和历史特征向量求余弦相似值
* ⑥ 对比相似值是否达到要求的阈值
* */
public boolean doSimilarity(String ordernessPassword, Set<String> historyOrdernessPasswords){
if (historyOrdernessPasswords==null | historyOrdernessPasswords.size()==0) return false ;//第一次登陆
if (historyOrdernessPasswords.contains(ordernessPassword)) return false; //输入的密码合历史密码有相同
//获取出字袋
List<Character> bagging = bagging(historyOrdernessPasswords);
//算出所有历史密码的特征向量集合
List<Integer[]> vectorQuantity=new ArrayList<>();
for (String password : historyOrdernessPasswords) {
Integer[] integers = thisTime(bagging, password);
vectorQuantity.add(integers);
}
//算出本次登录密码的特征向量
Integer[] integers = thisTime(bagging, ordernessPassword);
//本次登录密码的特征向量和历史特征向量求余弦相似值 并返回一个最大的值
Double aDouble = similarValues(vectorQuantity, integers);
//如果 相似值 小于 阈值 便为 true ;
return aDouble < threshold;
}
/**
* 先获取出字袋
* */
public List<Character> bagging(Set<String> historyOrdernessPasswords){
Set<Character> character=new HashSet<>();
//将历史密码转成一个一个字节存入set 集合去重;
for (String password : historyOrdernessPasswords) {
char[] chars = password.toCharArray();
for (char aChar : chars) {
character.add(aChar);
}
}
//将这些字节进行排序
List<Character> collect = character.stream().sorted().collect(Collectors.toList());
return collect;
}
/**
* 算出登录密码的特征向量
* */
public Integer[] thisTime(List<Character> bagging,String ordernessPassword){
Integer[] integers = new Integer[bagging.size()];
//获取当前登陆密码各个字符的出现次数
Map<Character,Integer> th=new HashMap<>();
for (char c : ordernessPassword.toCharArray()) {
int count=1;
if (th.containsKey(c)) count=th.get(c);
th.put(c,count);
}
for (int i=0;i<bagging.size();i++){
//三元运算符 判断map 的key是否有字袋里的这个值 有 将这个值出现的次数存到数组 没有存为0
integers[i]=th.containsKey(bagging.get(i)) ? th.get(bagging.get(i)) : 0 ;
}
return integers;
}
/**
* 本次登录密码的特征向量和历史特征向量求余弦相似值,并返回一个最大的值
* */
public Double similarValues(List<Integer[]> vectorQuantity,Integer[] integers){
List<Double> list=new ArrayList<>();
for (Integer[] integers1 : vectorQuantity) {
Double sum=0.0;
for (int i=0;i<integers.length;i++){
sum+= integers1[i]* integers[i];
}
//计算平方和
Integer powSum1 = Arrays.stream(integers1).map(i -> i * i).reduce((i1, i2) -> i1 + i2).get();
Integer powSum2 = Arrays.stream(integers).map(i -> i * i).reduce((i1, i2) -> i1 + i2).get();
//Math.sqrt(xxx) 开平方 得出与历史密码的相似度,存入集合
double v = sum / (Math.sqrt(powSum1) * Math.sqrt(powSum2));
list.add(v);
}
//对 集合进行排序 取最大的
Double aDouble = list.stream().sorted().collect(Collectors.toList()).get(list.size() - 1);
return aDouble;
}
//用假数据测试下
public static void main(String[] args) {
SimilarityEvaluate similarityEvaluate = new SimilarityEvaluate(0.98);
HashSet<String> pwds = new HashSet<>();
pwds.add("456vcfg");
pwds.add("ct12345");
pwds.add("hjk8901");
System.out.println(similarityEvaluate.doSimilarity("45vc6fg",pwds));
}
}