一、
- 想要具体了解基于熵权G1法的CBR相似案例筛选方法的,可以去看这篇论文:
- 这里举一个具体的例子来说明如何计算两件事的相似程度
- 第一件事的数组[1, 0, 0.5, 0.3, 0.8]
- 第二件事的数组[1, 1, 0.7, 0.1, 0.8]
那么相似程度是这样算的:
sim=(1-|1-1|)*0.2+(1-|0-1|)*0.2+(1-|0.5-0.7|)*0.2+(1-|0.3-0.1|)*0.2+(1-|0.8-0.8|)*0.2
这里的0.2是权重,由专家讨论得出的,当然是我自己取的。反正所有的0.2加起来等于1就行了,这里有5个0.2,加起来就是1。
用语言去描述公式,就是:
用1减去两者之差,然后乘以权重,对于数组中的每个元素,都进行这样的操作,最后将这些结果加起来就行了。
相似程度的取值范围是零到一:[0,1]
二、
-
看了上面的例子,你可能会问,水污染事件怎么和上面的数组关联起来呢?先看下面的两件水污染事件:
-
这里基本全是文字呀,怎么计算相似度?没错,文字不能用于计算,但是可以把文字和数字映射起来。以污染物毒性为例,文字可以和数字建立如下图的映射方式:
也就是事件一的“中等毒”,可以用0.5表示,事件二的低毒可以用0.3表示。 -
那么污染物类型和污染物来源怎么赋值?我们可以以事件一为参考,直接把事件一的污染物类型和污染物来源赋值为1,
其他事件(事件二),如果污染物类型与事件一的污染物类型相同,就赋值为1,否则赋值为0,同理,如果污染物来源与事件一的污染物来源相同,就赋值为1,否则赋值为0。 -
还有污染物超标倍数、与下游取水口方法这两个指标,可以按照(类比)下面的方法赋值:
三、代码
- 代码实现起来并不难,为一个水污染案例(事件)写一个实体类即可,其中数据库保存有一定数量的案例。
实体类如下(为了按照相似度排序,需要实现Comparable接口):
package domain;
/**
* @author laoyingyong
* @date: 2020-02-05 13:28
*/
public class Example implements Comparable<Example>
{
private Integer id;
private String name;
private String type;
private String source;
private Double multiple;
private Integer distance;
private String toxicity;
private String danger;
private String stability;
private String solubility;
private String volatility;
private String technology;
private Double sim;
public Double getSim() {
return sim;
}
public void setSim(Double sim) {
this.sim = sim;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public Double getMultiple() {
return multiple;
}
public void setMultiple(Double multiple) {
this.multiple = multiple;
}
public Integer getDistance() {
return distance;
}
public void setDistance(Integer distance) {
this.distance = distance;
}
public String getToxicity() {
return toxicity;
}
public void setToxicity(String toxicity) {
this.toxicity = toxicity;
}
public String getDanger() {
return danger;
}
public void setDanger(String danger) {
this.danger = danger;
}
public String getStability() {
return stability;
}
public void setStability(String stability) {
this.stability = stability;
}
public String getSolubility() {
return solubility;
}
public void setSolubility(String solubility) {
this.solubility = solubility;
}
public String getVolatility() {
return volatility;
}
public void setVolatility(String volatility) {
this.volatility = volatility;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
@Override
public String toString() {
return "Example{" +
"id=" + id +
", name='" + name + '\'' +
", type='" + type + '\'' +
", source='" + source + '\'' +
", multiple=" + multiple +
", distance=" + distance +
", toxicity='" + toxicity + '\'' +
", danger='" + danger + '\'' +
", stability='" + stability + '\'' +
", solubility='" + solubility + '\'' +
", volatility='" + volatility + '\'' +
", technology='" + technology + '\'' +
", sim=" + sim +
'}';
}
@Override
public int compareTo(Example example)
{
return this.sim<example.sim?1:this.sim>example.sim?-1:0;
}
}
- 再写一个工具类(因为用户输入的内容基本是都汉字),把汉字和数字建立起映射关系。当然也可以不写这个工具类,前端直接把数据发送到后台即可。
package util;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
/**
* @author laoyingyong
* @date: 2020-02-05 14:32
*/
public class ExampleUtils
{
public static double getMultipleNum(double n)
{
if(n>=100)
{ return 1;}
else if(n>=90)
{ return 0.9;}
else if(n>=80)
{return 0.8;}
else if(n>=70)
{return 0.7;}
else if(n>=60)
{return 0.6;}
else if(n>=50)
{return 0.5;}
else if(n>=40)
{return 0.4;}
else if(n>=30)
{return 0.3;}
else if(n>=20)
{return 0.2;}
else if(n>=0)
{return 0.1;}
else {System.out.println("倍数不能为负数!");return 0;}//倍数不可能为负数
}
public static double getDistanceNum(double n)
{
if(n>=270)
{ return 1;}
else if(n>=240)
{ return 0.9;}
else if(n>=210)
{return 0.8;}
else if(n>=180)
{return 0.7;}
else if(n>=150)
{return 0.6;}
else if(n>=120)
{return 0.5;}
else if(n>=90)
{return 0.4;}
else if(n>=60)
{return 0.3;}
else if(n>=30)
{return 0.2;}
else if(n>=0)
{return 0.1;}
else {System.out.println("距离不能为负数!");return 0;}//距离不可能为负数
}
public static double getToxicityNum(String toxicity)
{
if(toxicity.equals("微毒"))
{
return 0.1;
}
else if(toxicity.equals("低毒"))
{
return 0.3;
}
else if(toxicity.equals("中等毒"))
{
return 0.5;
}
else if(toxicity.equals("高毒"))
{
return 0.7;
}
else if(toxicity.equals("剧毒"))
{
return 0.9;
}
else
{
System.out.println("污染毒性等级有误!");
return 0;
}
}
public static double getDangerNum(String danger)
{
if(danger.equals("可燃"))
{
return 0.2;
}
else if(danger.equals("易燃"))
{
return 0.5;
}
else if(danger.equals("易燃易爆"))
{
return 0.8;
}
else
{
System.out.println("污染物危险等级有误!");
return 0;
}
}
public static double getStabilityNum(String stability)
{
if(stability.equals("不稳定"))
{
return 0.2;
}
else if(stability.equals("中等"))
{
return 0.5;
}
else if(stability.equals("稳定"))
{
return 0.8;
}
else
{
System.out.println("污染物稳定性等级有误!");
return 0;
}
}
public static double getSolubilityNum(String solubility)
{
if(solubility.equals("不溶于水"))
{
return 0.2;
}
else if(solubility.equals("微溶于水"))
{
return 0.5;
}
else if(solubility.equals("易溶于水"))
{
return 0.8;
}
else
{
System.out.println("污染物溶解性等级有误!");
return 0;
}
}
public static double getVolatilityNum(String volatility)
{
if(volatility.equals("不易挥发"))
{
return 0.2;
}
else if(volatility.equals("中等挥发"))
{
return 0.5;
}
else if(volatility.equals("易挥发"))
{
return 0.8;
}
else
{
System.out.println("污染物挥发性等级有误!");
return 0;
}
}
}
- 业务逻辑层(service层)的代码如下:
package service.impl;
import dao.ExampleDao;
import dao.impl.ExampleDaoImpl;
import domain.Example;
import service.ExampleService;
import util.ExampleUtils;
import java.text.DecimalFormat;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* @author laoyingyong
* @date: 2020-02-05 13:47
*/
public class ExampleServiceImpl implements ExampleService
{
ExampleDao dao=new ExampleDaoImpl();
@Override
public List<Example> findResembleExamples(String type, String source, double multiple, int distance, String toxicity, String danger, String stability, String solubility, String volatility)
{
double [] current=new double[9];//当前污染事件的数组
//给数组赋值
current[0]=1;
current[1]=1;
current[2]= ExampleUtils.getMultipleNum(multiple);//超标倍数
current[3]=ExampleUtils.getDistanceNum(distance);
current[4]=ExampleUtils.getToxicityNum(toxicity);
current[5]=ExampleUtils.getDangerNum(danger);
current[6]=ExampleUtils.getStabilityNum(stability);
current[7]=ExampleUtils.getSolubilityNum(solubility);
current[8]=ExampleUtils.getVolatilityNum(volatility);
List<Example> list = dao.findResembleExamples();
LinkedList<Example> exampleLinkedList=new LinkedList<>();
for (Example example : list)
{
String type1 = example.getType();
String source1 = example.getSource();
Double multiple1 = example.getMultiple();
Integer distance1 = example.getDistance();
String toxicity1 = example.getToxicity();
String danger1 = example.getDanger();
String stability1 = example.getStability();
String solubility1 = example.getSolubility();
String volatility1 = example.getVolatility();
double [] history=new double[9];//历史污染事件的数组
//给数组赋值
if(type1.equals(type))//如果污染物类型相同就赋值为1,否则为0
{
history[0]=1;
}
else
{
history[0]=0;
}
if(source1.equals(source))//如果污染物来源相同就赋值为1,否则赋值为0
{
history[1]=1;
}
else
{
history[1]=0;
}
history[2]= ExampleUtils.getMultipleNum(multiple1);//超标倍数
history[3]=ExampleUtils.getDistanceNum(distance1);//距离
history[4]=ExampleUtils.getToxicityNum(toxicity1);//毒性
history[5]=ExampleUtils.getDangerNum(danger1);//危险程度
history[6]=ExampleUtils.getStabilityNum(stability1);//稳定性
history[7]=ExampleUtils.getSolubilityNum(solubility1);//溶解性
history[8]=ExampleUtils.getVolatilityNum(volatility1);//挥发性
double sim=(1-Math.abs(current[0]-history[0]))*0.1123+//根据熵权G1法计算当前污染事件与历史污染事件的相似度
(1-Math.abs(current[1]-history[1]))*0.1123+
(1-Math.abs(current[2]-history[2]))*0.1123+
(1-Math.abs(current[3]-history[3]))*0.1123+
(1-Math.abs(current[4]-history[4]))*0.1112+
(1-Math.abs(current[5]-history[5]))*0.1112+
(1-Math.abs(current[6]-history[6]))*0.1109+
(1-Math.abs(current[7]-history[7]))*0.1088+
(1-Math.abs(current[8]-history[8]))*0.1088;
DecimalFormat decimalFormat=new DecimalFormat("0.0000");//保留四位小数
String format = decimalFormat.format(sim);
double v = Double.parseDouble(format);
example.setSim(v);//设置相似度
if(v>0.5)//如果相似度大于0.5的话,就存入集合中
{
exampleLinkedList.add(example);
}
}
Collections.sort(exampleLinkedList);//按照相似度从大到小排序,Example这个实体类要实现Comparable接口才行
System.out.println("排序后"+exampleLinkedList);
return exampleLinkedList;
}
}
四、效果展示
- 前端用户的输入,大部分都是下拉列表select
- 后台经过熵权计算,把相似度比较大的数据发送到前台,相似度按照从大到小的顺序进行排序。