基于规则系统的表示与推理的动物专家系统(使用正向推理和反向推理两种方法)
解题思路
- 正向推理:
从用户提供的初始已知事实出发,从规则集中选出一条可适用的规则进行推理,并将推理到的结论加入事实库,重复上述过程,直到求得了所要的解或者规则集无法在扩展为止。算法描述如下:
(1) DATA<-初始数据库
(2) Util DATA 满足结束条件以前,do
(3) Begin
a) 在规则集中选择一条适用于DATA的规则
b) 将该规则得到结果加入DATA中
End - 反向推理:
假设一个目标,通过规则找到推出该目标的证据,若所需的这些证据都在用户给事实库中,就说明假设成立,若无论如何都找不到支持假设的证据,说明原假设不成立,此时需要另做假设。算法描述如下:
(1)做出假设G
(2)检查G是否在事实数据库中,若在则假设成功返回该假设匹配成功。若不在,则进行下一步。
(3)从规则库中寻找能推出该假设的规则。对找到的每一条规则的证据(即规则的左部)分别作为假设递归调用该函数。
(4)上述调用中若有结果为匹配成功则说明该假设是可以成功推出的,返回匹配成功。
其实是一个深度搜索的过程代码
Inference.java
package com.xiao;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import java.awt.*;
import java.util.*;
import java.util.List;
public class Inference {
//数据库
String[] FACTS = new String[]{/*1*/"反刍动物",/*2*/"蹄类动物",/*3*/"哺乳动物",
/*4*/"目视前方",/*5*/"有爪子",/*6*/"有犬齿",/*7*/"吃肉"
,/*8*/"下蛋",/* 9*/"会飞",/*10*/"有羽毛",
/*1l*/"有蹄",/*12*/"肉食动物",/*13*/"鸟类",/*14*/"产奶",/*l5*/"有毛发", /*16*/"善飞",/* 17*/"黑白色",/*18*/"会游泳",
/*19*/"长腿",/*20*/"长脖子",/*21*/"有黑色条纹",/*22*/"有暗斑点",/*23*/"黄褐色",/*24*/"信天翁",
/*25*/"企鹅",/*26*/"鸵鸟",/*27*/"斑马",/*28 */"长颈鹿",/* 29 */"老虎",/*30*/"金钱豹"};
public int[][] RULE_pre = {{15}, {14}, {10}, {9, 8}, {7}, {6, 5, 4}, {3, 2}, {3, 1},
{23, 22, 12, 3}, {23, 21, 12, 3}, {22, 20, 19, 11},
{21, 11}, {20, 19, 17, 13, -9}, {18, 17, 13, -9}, {16, 13},
};
public int[] RULE_after = {3, 3, 13, 13, 12, 12, 2, 2, 30, 29, 28, 27, 26, 25, 24};
public HashSet<Integer> curFacts;
public HashSet<Integer> use_rules;
public HashSet<Integer> post_use_rules;
private TextArea t1;
private TextArea t2;
private StringBuffer forPanel;
private StringBuffer process;
Inference(TextArea t1,TextArea t2) {
this.t1 = t1;
this.t2 = t2;
forPanel = new StringBuffer();
process = new StringBuffer();
printFacts_Rules();
//System.out.println("请输入特征数字以,隔开");
forPanel.append("请输入特征数字以,隔开\n");
show();
}
public void show(){
t1.setText(forPanel.toString());
t2.setText(process.toString());
}
public void in(String input){
String[] sp = input.split(",");
int[] in = new int[sp.length];
for(int i = 0;i<in.length;i++)
in[i] = Integer.parseInt(sp[i]);
use_rules = new HashSet<>();
post_use_rules = new HashSet<>();
curFacts = new HashSet<>();
for(int i:in){
curFacts.add(i);
if(i>0){forPanel.append(FACTS[i-1]+" ");}
else{forPanel.append("不"+FACTS[Math.abs(i)-1]+" ");}
}//System.out.print(FACTS[i-1]+" ");}
forPanel.append("\n");//System.out.println();
show();
}
void printFacts_Rules(){
for(int i=0;i<FACTS.length;i++){
if(i%10==0) forPanel.append("\n");//System.out.println();
forPanel.append((i+1)+" "+FACTS[i]+" ");//System.out.print(i+1+" "+FACTS[i]+" ");
}
forPanel.append("\n");//System.out.println();
for(int i=0;i<RULE_pre.length;i++){
forPanel.append("rule"+(i+1)+": ");//System.out.print("rule"+(i+1)+": ");
for(int j:RULE_pre[i]){
if(j>0)
forPanel.append(FACTS[Math.abs(j)-1]+" ");//System.out.print(FACTS[Math.abs(j)-1]+" ");
else
forPanel.append("不"+FACTS[Math.abs(j)-1]+" ");
}
forPanel.append("----->"+FACTS[RULE_after[i]-1]+" ");//System.out.print("----->"+FACTS[RULE_after[i]-1]+" ");
if(i%3==0) forPanel.append("\n");//System.out.println();
}
forPanel.append("\n");//System.out.println();
}
private void printCurFacts(){
process.append("当前事实库");//System.out.print("当前事实库");
int j = 0;
for(int i:curFacts){
if(j%5==0) process.append("\n");//System.out.println();
j++;
if(i>0)
process.append(FACTS[i-1]+ " ");//System.out.print(FACTS[i-1]+ " ");
else process.append("不"+FACTS[Math.abs(i)-1]+ " ");
}
process.append("\n");//System.out.println();
}
private void printCurUseRules(){
for(int i:use_rules){
if(!post_use_rules.contains(i)){
process.append("rule"+(i+1)+":");//System.out.print("rule"+(i+1)+":");
for(int j:RULE_pre[i])
if(j>0)
process.append(FACTS[Math.abs(j)-1]+" ");//System.out.print(FACTS[Math.abs(j)-1]+" ");
else process.append("不"+FACTS[Math.abs(j)-1]+" ");
process.append("----->"+FACTS[RULE_after[i]-1]+" ");//System.out.print("----->"+FACTS[RULE_after[i]-1]+" ");
post_use_rules.add(i);
}
}
process.append("\n");//System.out.println();
}
/**
* 正向推理
* */
public void forword(){
printCurFacts();
HashSet<Integer> indexs;
while (true){
indexs = find(curFacts);
printCurUseRules();
int size = curFacts.size();
for(int i:indexs)
curFacts.add(i);
if(curFacts.size() > size){
printCurFacts();
show();
}
if(curFacts.size() == size){ break;}
}
StringBuffer res = new StringBuffer();
boolean flags = false;
process.append("最终结果如下:\n");//System.out.println("最终结果如下:");
for(int i=24;i<=30;i++){
if(curFacts.contains(i)){ res.append(FACTS[i-1]+" "); flags=true;}
}
if(flags) process.append("推理成功,该可能动物为:"+res.toString()+"\n");
else process.append("推理不成功\n");//System.out.println("推理不成功"); //System.out.println("推理成功,该可能动物为:"+res);
show();
}
/**
* 传入curFacts,返回可推出结论的下标集合
* curFacts是否包含规则左侧的证据
* */
private HashSet<Integer> find(HashSet<Integer> input) {
HashSet<Integer> res = new HashSet<>();
HashSet<Integer> hashset = new HashSet<>();
for(Object i:input.toArray()){
hashset.add(Integer.parseInt(i.toString()));
}
for (int i = 0; i < RULE_pre.length; i++) {
int j = 0;
for(j = 0; j < RULE_pre[i].length; j++)
{
if(!hashset.contains(RULE_pre[i][j]))
break;
}
if(j == RULE_pre[i].length){ res.add(RULE_after[i]);use_rules.add(i);}
}
return res;
}
/**
* 反向推理
* */
public void backword(){
for(int i=24;i<=30;i++){
if(dfs(i)){
process.append("推理成功,该动物可能为:"+FACTS[i-1]+"\n");//System.out.println("推理成功,该动物可能为:"+FACTS[i-1]);
break;
}else{
process.append("推理不成功,该动物不为:"+FACTS[i-1]+"\n");//System.out.println("推理不成功,该动物不为:"+FACTS[i-1]);
}
}
show();
}
/**
* 寻找num在规则右部出现的规则
* */
private List<Integer> InRULE_after(int num){
List<Integer> res = new ArrayList<>();
for(int i=0;i<RULE_after.length;i++){
if(RULE_after[i] == num) res.add(i);
}
return res;
}
/**
* 深度遍历
* */
private boolean dfs(int num){
String temp = num>0?"":"不";
process.append("假设为"+temp+FACTS[Math.abs(num)-1]+"\n");
if(curFacts.contains(num)) return true;
else{
List<Integer> indexs = InRULE_after(num);
if(indexs.size()==0){ return false;}
else {
for(int i:indexs){
boolean flag = true;
for(int j:RULE_pre[i]){
String a = j>0?"":"不";
if(!dfs(j)){ flag = false; process.append(a+FACTS[Math.abs(j)-1]+ "假设失败\n");}
else process.append(a+FACTS[Math.abs(j)-1]+ "假设成功\n");
}
if(flag){return true;}
}
return false;
}
}
}
}
Main.java
package com.xiao;
import javax.swing.*;
import java.awt.Dimension;
import java.awt.TextArea;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Main extends JFrame {
private JTextField textField;
private Inference inference ;
private TextArea textArea_1;
private TextArea textArea;
private JButton button;
private JButton button_1;
private boolean isNew = true;
public Main() {
setSize(new Dimension(931, 654));
setResizable(false);
getContentPane().setLayout(null);
textArea = new TextArea();
textArea.setEditable(false);
textArea.setRows(50);
textArea.setBounds(50, 29, 830, 204);
getContentPane().add(textArea);
textField = new JTextField();
textField.setBounds(165, 256, 489, 21);
getContentPane().add(textField);
textField.setColumns(10);
button = new JButton("\u6B63\u5411\u63A8\u7406");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(!isNew) inference = new Inference(textArea,textArea_1);
String input = textField.getText().toString();
if(input.equals("")) textArea_1.setText("请输入特征");
else{ inference.in(input); inference.forword(); isNew=false;}
}
});
button.setBounds(219, 287, 93, 23);
getContentPane().add(button);
button_1 = new JButton("\u9006\u5411\u63A8\u7406");
button_1.setBounds(477, 287, 93, 23);
button_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(!isNew) inference = new Inference(textArea,textArea_1);
String input = textField.getText().toString();
if(input.equals("")) textArea_1.setText("请输入特征");
else{ inference.in(input); inference.backword(); isNew=false;}
}
});
getContentPane().add(button_1);
textArea_1 = new TextArea();
textArea_1.setRows(50);
textArea_1.setEditable(false);
textArea_1.setBounds(50, 334, 820, 280);
getContentPane().add(textArea_1);
setVisible(true);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
inference = new Inference(textArea,textArea_1);
}
public static void main(String[] args) {
Main m = new Main();
}
}
运行结果
输入特征4,5,6,14,22,23 点击正向推理
输入17,18,10,-9点击逆向推理
输入1,2,4,5,6
点击正向推理
点击逆向推理