密码相似度评估(用到余弦相识度的数学算法知识)

密码相似度评估

首先是对余弦相识度的数学算法知识的回忆

首先我们从余弦函数说起
密码相似度评估(用到余弦相识度的数学算法知识)_第1张图片

30度45度60度90度的余弦、正切、正弦、余切所对应的值如图所示:密码相似度评估(用到余弦相识度的数学算法知识)_第2张图片

余弦定理密码相似度评估(用到余弦相识度的数学算法知识)_第3张图片

在二维的坐标下对比的余弦

密码相似度评估(用到余弦相识度的数学算法知识)_第4张图片
把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²

进行化减后得出
密码相似度评估(用到余弦相识度的数学算法知识)_第5张图片

X12*X22 + X21*X11 / 根号下 X11² + X12² * 根号下 X21² + X22²

在三维的坐标下对比的余弦

密码相似度评估(用到余弦相识度的数学算法知识)_第6张图片

密码相似度评估(用到余弦相识度的数学算法知识)_第7张图片

X12 * X22 + X21 * X11 + X13* X23 / 根号下 X11² + X12²+ X13² * 根号下 X21² + X22² + X23²

在N维的坐标下对比的余弦

密码相似度评估(用到余弦相识度的数学算法知识)_第8张图片

数学问题解决了 ,现在转成现实问题的解决方案

密码相似度评估(用到余弦相识度的数学算法知识)_第9张图片

那么现在新的问题来了,密码相似度评估 我们提取密码的什么特征作为向量那

密码相似度评估(用到余弦相识度的数学算法知识)_第10张图片
根据我们常人更改密码的习惯,我们很好会有将密码改的和之前相比面目全非的,因为这样的话会严重增加了我们的记忆成本,在现在网络时代各种账号密码存在的情况,很容易造成记错和忘记的情况,所以我们修改密码通常都是在之前密码上做增添或修改,以便于记忆和回想!

思路整理:
所以我们将所有的历史密码所用到的字节全部收集进行排序去重,作为字袋!
然后那密码和字袋进行如图比对 ,形成向量坐标的存在,
然后将输入的密码向量和历史密码向量利用前面我们推导出来的余弦相似来算出相似度!
进行密码相似度评估

思路清晰了,那就开始代码实现

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));
    }
}

你可能感兴趣的:(密码相似度评估(用到余弦相识度的数学算法知识))