动物识别——人工智能

实验三 产生式系统推理

一、实验目的

本实验课程是计算机、智能、物联网等专业学生的一门专业课程,通过实验,帮助学生更好地掌握人工智能相关概念、技术、原理、应用等;通过实验提高学生编写实验报告、总结实验结果的能力;使学生对智能程序、智能算法等有比较深入的认识。
1.掌握人工智能中涉及的相关概念、算法;
2.熟悉人工智能中的知识表示方法;
3.掌握问题表示、求解及编程实现;
4.理解产生式系统的结构原理与实际应用;
5.掌握产生式规则表示及规则库组建的实现方法;
6.熟悉和掌握产生式系统的运行机制,掌握基于规则推理的基本方法。

二、基本要求

1.实验前,复习《人工智能》课程中的有关内容。
2.准备好实验数据。
3.编程要独立完成,程序应加适当的注释。
4.完成实验报告。

三、实验软件

使用C或C++(Visual studio或其它开发环境)(不限制语言使用)。

四、实验内容:

以动物识别系统为例,用选定的编程语言建造规则库和综合数据库,开发能进行正确的正向推理或反向推理的推理机。
正向推理过程
从已知事实出发,通过规则库求得结论,或称数据驱动方式。推理过程是:
规则集中的规则前件与事实库中的事实进行匹配,得匹配的规则集合。
从匹配规则集合中选择一条规则作为使用规则。
执行使用规则的后件,将该使用规则的后件送入事实库中。
重复这个过程直至达到目标。

1 动物分类规则集
(1)若某动物有奶,则它是哺乳动物。
(2)若某动物有毛发,则它是哺乳动物。
(3)若某动物有羽毛,则它是鸟。
(4)若某动物会飞且生蛋,则它是鸟。
(5)若某动物是哺乳动物且有爪且有犬齿且目盯前方,则它是食肉动物。
(6)若某动物是哺乳动物且吃肉,则它是食肉动物。
(7)若某动物是哺乳动物且有蹄,则它是有蹄动物。
(8)若某动物是有蹄动物且反刍食物,则它是偶蹄动物。
(9)若某动物是食肉动物且黄褐色且有黑色条纹,则它是老虎。
(10)若某动物是食肉动物且黄褐色且有黑色斑点,则它是金钱豹。
(11)若某动物是有蹄动物且长腿且长脖子且黄褐色且有暗斑点,则它是长颈鹿。
(12)若某动物是有蹄动物且白色且有黑色条纹,则它是斑马。
(13)若某动物是鸟且不会飞且长腿且长脖子且黑白色,则它是驼鸟。
(14)若某动物是鸟且不会飞且会游泳且黑白色,则它是企鹅。
(15)若某动物是鸟且善飞且不怕风浪,则它是海燕。
下面是该规则集所形成的(部分)推理网络:
图1 动物识别系统部分推理网络

1.2 问题描述

由上述动物识别规则组成规则库,推理机采用正向推理算法或反向推理算法,实现对动物的查询。
如给出初始事实:
F1:某动物有毛发
F2:吃肉
F3:黄褐色
F4:有黑色条纹
目标条件为:该动物是什么?
3 规则库扩充 (选做)
在上述规则集(Ⅰ)基础上增加以下规则集(Ⅱ):
(1)兔子:有毛发,有奶,善跳跃,唇裂;
(2)猫:有毛发,有奶,善捕鼠,脚有肉垫;
(3)犀牛:有毛发,有奶,鼻子上有角,褐色,皮糙肉后,皮糙肉厚,有蹄;
(4)熊猫:有毛发,有奶,黑眼圈,四肢短小;
(5)鹦鹉:鸟类,上嘴鹰钩,会模仿人说话;
(6)鸭子:鸟类,腿短,嘴扁平,善潜水游泳;
(7)鹰:鸟类,上嘴鹰钩,有爪,吃肉;
(8)鸭子:有羽毛,卵生,善游泳,嘴扁平,腿短;
(9)鹅:有羽毛,卵生,善潜水游泳,白色或黑色,颈长,嘴大,腿长,颈部有肉只凸起;
(10)鸦:有羽毛,卵生,黑色,嘴大;
(11)鹰:有羽毛,卵生,有爪,吃肉,上嘴鹰钩;
(12)鹦鹉:有羽毛,卵生,上嘴鹰钩,能模仿人说话;
(13)青蛙:卵生,生活在水中,生活在陆地,有皮肤呼吸,用肺呼吸,皮肤光滑,吃昆虫,会变色;
(14)蝾螈:卵生,生活在水中,生活在陆地,有皮肤呼吸,用肺呼吸,吃昆虫,皮肤粗糙,四肢扁,背部黑色;
(15)蟾蜍:卵生,生活在水中,生活在陆地,有皮肤呼吸,用肺呼吸,吃昆虫,皮肤粗糙;
(16)比目鱼:用鳃呼吸,身体有鳍,生活在海洋中,身体扁平,两眼在头部同侧;
(17)鲫鱼:用鳃呼吸,身体有鳍,生活在淡水中,身体扁平,头高尾部窄;
(18)蛇:生活在陆地,用肺呼吸,胎生,身体有鳞或甲,身体圆而细长,吃小动物;
(19)壁虎:生活在陆地,用肺呼吸,胎生,身体有鳞或甲,有四肢,尾巴细长易断,吃昆虫;
(20)乌龟:生活在陆地,用肺呼吸,胎生,身体有鳞或甲,身体圆而扁,有坚硬的壳;
(21)玳瑁:生活在陆地,用肺呼吸,胎生,身体有鳞或甲,壳为黄褐色,皮肤光滑,有黑斑;
(22)鳄鱼:生活在陆地,用肺呼吸,胎生,身体有鳞或甲,有四肢,善游泳,皮硬黑褐色。
要求在动物分类规则集(Ⅰ)的基础上添加上述22条知识,共构成29种动物的知识库系统,对原有动物分类系统进行扩充和修改。

五、实验程序组成

(1)使用的结构
Rule类,类中存放一个int类型的指针和重载了一个运算符 == ,用于后续与int类型的数组作比较。
(2)使用的各种函数
//用于中间状态操作的函数
Bool include(Rule rule, int *ch); //判断ch中含有rule全部元素
Void clean(Rule rule, int *ch); //删除ch中与rule相同的元素
bool zero(int *ch); //判断ch中是否全部元素都为0
Bool check(int *ch, int *p); //判断ch中是否还含有p中元素
//规则库函数
Int rule1(int *ch); //一级规则,可以直接由条件推理出
Int rule2(int *ch, int rule1); //二级规则,需要结合一级推理结果
Int rule3(int *ch, int rule2); //三级规则,需要结合二级推理结果
//推理结果处理函数
Int result(int *ch, int rule3); //根据rule3的结果进行分析
Void show(int a); //根据推理得出的序号输出相应文字
(3)部分实验代码:

//规则库
int rule1(int* ch) {
	if (!zero(ch)) {
		//有奶->哺乳动物
		Rule rule1;
		int t1=1;
		rule1.rule = &t1;
		if (include(rule1, ch)) {
			clean(rule1, ch);
			return 24;
		}
		//有毛发->哺乳动物
		Rule rule2;
		int t2=2;
		rule2.rule = &t2;
		if (include(rule2, ch)) {
			clean(rule2, ch);
			return 24;
		}
		//有羽毛->鸟
		Rule rule3;
		int t3 = 3;
		rule3.rule = &t3;
		if (include(rule3, ch)) {
			clean(rule3, ch);
			return 25;
		}
		//会飞,能生蛋->鸟
		Rule rule4;
		int t[2] = { 4,5 };
		rule4.rule = &t[0];
		if (include(rule4, ch)) {
			clean(rule4, ch);
			return 25;
		}
	}
	else
		return 0;
}

int rule2(int* ch, int rule1) {
	if (!zero(ch)) {
		switch (rule1) {
			//哺乳动物
		case 24: {
			//有爪,有犬齿,目盯前方->食肉动物
			Rule rule5;
			int t[3] = { 6,7,8 };
			rule5.rule = &t[0];
			if (include(rule5, ch)) {
				clean(rule5, ch);
				return 26;
			}
			//吃肉->食肉动物
			Rule rule6;
			int t1 = 9;
			rule6.rule = &t1;
			if (include(rule6, ch)) {
				clean(rule6, ch);
				return 26;
			}
			//有蹄->有蹄动物
			Rule rule7;
			int t2 = 10;
			rule7.rule = &t2;
			if (include(rule7, ch)) {
				clean(rule7, ch);
				return 27;
			}
			return 24;
		};
			   //鸟
		case 25: {
			//不会飞,长腿,长脖子,黑白色->鸵鸟
			Rule rule8;
			int t[4] = { 14,15,19,20 };
			rule8.rule = &t[0];
			if (include(rule8, ch)) {
				clean(rule8, ch);
				return 33;
			}
			//不会飞,会游泳,黑白色->企鹅
			Rule rule9;
			int t1[3] = { 19,20,21 };
			rule9.rule = &t1[0];
			if (include(rule9, ch)) {
				clean(rule9, ch);
				return 34;
			}
			//善飞,不怕风浪->海燕
			Rule rule10;
			int t2[2] = { 22,23 };
			rule10.rule = &t2[2];
			if (include(rule10, ch)) {
				clean(rule10, ch);
				return 35;
			}
			return 25;
		};
		}
	}
	else
		return rule1;
}

int rule3(int* ch, int rule2) {
	if (!zero(ch)) {
		switch (rule2) {
			//食肉动物
		case 26: {
			//黄褐色,黑色条纹->老虎
			Rule rule11;
			int t1[2] = { 12,17 };
			rule11.rule = &t1[0];
			if (include(rule11, ch)) {
				clean(rule11, ch);
				return 29;
			}
			//黄褐色,黑色斑点->金钱豹
			Rule rule12;
			int t2[2] = { 12,13 };
			rule12.rule = &t2[0];
			if (include(rule12, ch)) {
				clean(rule12, ch);
				return 30;
			}
			return 26;
		};
			   //有蹄动物
		case 27: {
			//反刍->偶蹄动物
			Rule rule13;
			int t1 = 11;
			rule13.rule = &t1;
			if (include(rule13, ch)) {
				clean(rule13, ch);
				return 28;
			}
			//长腿,长脖子,黄褐色,暗斑点->长颈鹿
			Rule rule15;
			int t3[4] = { 12,14,15,16 };
			rule15.rule = &t3[0];
			if (include(rule15, ch)) {
				clean(rule15, ch);
				return 31;
			}
			//白色,黑色条纹->斑马
			Rule rule14;
			int t4[2] = { 17,18 };
			rule14.rule = &t4[0];
			if (include(rule14, ch)) {
				clean(rule14, ch);
				return 32;
			}
			return 27;
		};
		}
		return rule2;
	}
	else
		return rule2;
}

六、实验结果分析:

初始界面:
在这里插入图片描述

输入题设条件:
在这里插入图片描述

得出结果:
在这里插入图片描述

实验心得:

①实验中采用了对规则的分级保证了推理的中间过程的完善性,不过这也使得本程序中的可用规则全为最基本的规则,若是从中间过程开始的推理,如推理条件以哺乳动物为基本条件的推理,需要用于自行推理出哺乳动物的基本条件。②规则库与记录用户输入条件的数组以指针*ch的形式存放,原本在确定条件数量的时候想用ch[]!=NULL的,可是会出现越界的情况,后来观察错误发现越界后数值都十分大或小,故重新设置边界条件为-50~50用来计算条件数量,也算一种取巧。

完整代码:

#include<iostream>
using namespace std;

//单条规则形式
class Rule {
public:
	int *rule;
	bool operator ==(int *ch);
};
bool Rule::operator==(int *ch) {
	int i = 0;
	while (ch[i] > -50 && ch[i] < 50) {
		if (rule[i] != ch[i])
			return true;
		i++;
	}
	return false;
}

//读取拥有函数
bool include(Rule rule,int *ch) {
	int rulelong=0, same=0;
	while (-50<rule.rule[rulelong] && rule.rule[rulelong]<50)
		rulelong++;;
	//检测ch有效长度内使用含有与rule数组相同数量的元素
	for (int i = 0; ch[i]>-50 && ch[i]<50; i++) {
		for (int m = 0; m < rulelong; m++) {
			if (rule.rule[m] == ch[i])
				same++;		
		}
	}
	if (same == rulelong)
		return true;
	else 
		return false;
}
//删除已使用过的条件:在ch中删除相应规则rule1
void clean(Rule rule,int* ch) {	
	int rulelong = 0, i = 0;
	while (rule.rule[rulelong] > -50 && rule.rule[rulelong] <50)                 
		rulelong++;
	while (ch[i] > -50 && ch[i] < 50) {
		int m = 0;
		while (m < rulelong) {
			if (rule.rule[m] == ch[i]) {
				ch[i] = 0;
				break;
			}
			m++;
		}
		i++;
	}
}
//全0判断
bool zero(int* ch) {
	int i = 0;
	while (ch[i] > -50 && ch[i] < 50) {
		if (ch[i] != 0)
			return false;
		i++;
	}
	return true;
}
//重复元素检查:ch中是否还含有p中的元素,true为可用重复,false为不可用重复
bool check(int* ch,int *p) {
	int same = 0,chlong=0;
	while (ch[chlong] >-50 && ch[chlong]<50)
		chlong++;
	//复制ch数组
	int* chcopy=new int[chlong];
	for (int i = 0; i < chlong; i++) {
		chcopy[i] = ch[i];
	}
	//先将0项加入same值中
	for (int i = 0; i < chlong; i++)
		if (chcopy[i] == 0)
			same++;
	//做相同分析
	for (int i = 0; i<chlong; i++)
		for (int m = 0; -50<p[m] && p[m]<50; m++)
			if (chcopy[i] == p[m] ) {
				same++;
				chcopy[i] = 0;
			}
	//比较same和ch长度
	if (same == chlong)
		return true;
	else
		return false;
}


//规则库
int rule1(int* ch) {
	if (!zero(ch)) {
		//有奶->哺乳动物
		Rule rule1;
		int t1=1;
		rule1.rule = &t1;
		if (include(rule1, ch)) {
			clean(rule1, ch);
			return 24;
		}
		//有毛发->哺乳动物
		Rule rule2;
		int t2=2;
		rule2.rule = &t2;
		if (include(rule2, ch)) {
			clean(rule2, ch);
			return 24;
		}
		//有羽毛->鸟
		Rule rule3;
		int t3 = 3;
		rule3.rule = &t3;
		if (include(rule3, ch)) {
			clean(rule3, ch);
			return 25;
		}
		//会飞,能生蛋->鸟
		Rule rule4;
		int t[2] = { 4,5 };
		rule4.rule = &t[0];
		if (include(rule4, ch)) {
			clean(rule4, ch);
			return 25;
		}
	}
	else
		return 0;
}

int rule2(int* ch, int rule1) {
	if (!zero(ch)) {
		switch (rule1) {
			//哺乳动物
		case 24: {
			//有爪,有犬齿,目盯前方->食肉动物
			Rule rule5;
			int t[3] = { 6,7,8 };
			rule5.rule = &t[0];
			if (include(rule5, ch)) {
				clean(rule5, ch);
				return 26;
			}
			//吃肉->食肉动物
			Rule rule6;
			int t1 = 9;
			rule6.rule = &t1;
			if (include(rule6, ch)) {
				clean(rule6, ch);
				return 26;
			}
			//有蹄->有蹄动物
			Rule rule7;
			int t2 = 10;
			rule7.rule = &t2;
			if (include(rule7, ch)) {
				clean(rule7, ch);
				return 27;
			}
			return 24;
		};
			   //鸟
		case 25: {
			//不会飞,长腿,长脖子,黑白色->鸵鸟
			Rule rule8;
			int t[4] = { 14,15,19,20 };
			rule8.rule = &t[0];
			if (include(rule8, ch)) {
				clean(rule8, ch);
				return 33;
			}
			//不会飞,会游泳,黑白色->企鹅
			Rule rule9;
			int t1[3] = { 19,20,21 };
			rule9.rule = &t1[0];
			if (include(rule9, ch)) {
				clean(rule9, ch);
				return 34;
			}
			//善飞,不怕风浪->海燕
			Rule rule10;
			int t2[2] = { 22,23 };
			rule10.rule = &t2[2];
			if (include(rule10, ch)) {
				clean(rule10, ch);
				return 35;
			}
			return 25;
		};
		}
	}
	else
		return rule1;
}

int rule3(int* ch, int rule2) {
	if (!zero(ch)) {
		switch (rule2) {
			//食肉动物
		case 26: {
			//黄褐色,黑色条纹->老虎
			Rule rule11;
			int t1[2] = { 12,17 };
			rule11.rule = &t1[0];
			if (include(rule11, ch)) {
				clean(rule11, ch);
				return 29;
			}
			//黄褐色,黑色斑点->金钱豹
			Rule rule12;
			int t2[2] = { 12,13 };
			rule12.rule = &t2[0];
			if (include(rule12, ch)) {
				clean(rule12, ch);
				return 30;
			}
			return 26;
		};
			   //有蹄动物
		case 27: {
			//反刍->偶蹄动物
			Rule rule13;
			int t1 = 11;
			rule13.rule = &t1;
			if (include(rule13, ch)) {
				clean(rule13, ch);
				return 28;
			}
			//长腿,长脖子,黄褐色,暗斑点->长颈鹿
			Rule rule15;
			int t3[4] = { 12,14,15,16 };
			rule15.rule = &t3[0];
			if (include(rule15, ch)) {
				clean(rule15, ch);
				return 31;
			}
			//白色,黑色条纹->斑马
			Rule rule14;
			int t4[2] = { 17,18 };
			rule14.rule = &t4[0];
			if (include(rule14, ch)) {
				clean(rule14, ch);
				return 32;
			}
			return 27;
		};
		}
		return rule2;
	}
	else
		return rule2;
}


//推理结果处理
int result(int *ch,int rule3) {
	//三重推理后还有剩余元素
	if (!zero(ch)) {
		//剩余有效重复元素正常返回,无效重复元素返回0
		switch (rule3) {
		case 24: {
			int t1[2] = { 1,2 };
			int* p1 = &t1[0];
			if (check(ch, p1))                                 //未通过check判定
				return 24;
			else
				return 0;          //查无此动物
		}
		case 25: {
			int t2[3] = { 3,4,5 };
			int* p2 = &t2[0];
			if (check(ch, p2))
				return 25;
			else
				return 0;
		}
		case 26: {
			int t3[6] = { 1,2,6,7,8,9 };
			int* p3 = &t3[0];
			if (check(ch, p3))
				return 26;
			else
				return 0;		
		}
		case 27: {
			int t4[] = { 1,2,10 };
			int* p4 = &t4[0];
			if (check(ch, p4))
				return 27;
			else
				return 0;		
		}
		case 28: {
			int t5[] = { 1,2,10,11 };
			int* p5 = &t5[0];
			if (check(ch, p5))
				return 28;
			else
				return 0;	
		}
		case 29: {
			int t6[] = {1,2,6,7,8,9,12,17};
			int* p6 = &t6[0];
			if (check(ch, p6))
				return 29;
			else
				return 0;		
		}
		case 30: {
			int t7[] = {1,2,6,7,8,9,12,13};
			int* p7 = &t7[0];
			if (check(ch, p7))
				return 30;
			else
				return 0;		
		}
		case 31: {
			int t8[] = {1,2,10,12,14,15,16};
			int* p8 = &t8[0];
			if (check(ch, p8))
				return 31;
			else
				return 0;	
		}
		case 32: {
			int t9[] = {1,2,10,17,18};
			int* p9 = &t9[0];
			if (check(ch, p9))
				return 32;
			else
				return 0;		
		}
		case 33: {
			int t10[] = {3,4,5,14,15,19,20};
			int* p10 = &t10[0];
			if (check(ch, p10))
				return 33;
			else
				return 0;		
		}
		case 34: {
			int t11[] = {3,4,5,19,20,21};
			int* p11 = &t11[0];
			if (check(ch, p11))
				return 34;
			else
				return 0;		
		}
		case 35: {
			int t12[] = { 3,4,5,22,23 };
			int* p12 = &t12[0];
			if (check(ch, p12))
				return 35;
			else
				return 0;		
		}		
		}	
	}
	//三种推理后无剩余元素
	else 
	return rule3;
}
//根据返回值进行输出
void show(int a){
	switch (a) {
	case 0: {
		cout << "根据给出条件,数据库中不存在此类动物,请重新输入" << endl;
		break;
	}
	case 24: {
		cout << "根据给出条件,所查询出结果为:哺乳动物" << endl;
		break;	
	}
	case 25: {
		cout << "根据给出条件,所查询出结果为:鸟" << endl;
		break;
	}
	case 26: {
		cout << "根据给出条件,所查询出结果为:食肉动物" << endl;
		break;
	}
	case 27: {
		cout << "根据给出条件,所查询出结果为:有蹄动物" << endl;
		break;
	}
	case 28: {
		cout << "根据给出条件,所查询出结果为:偶蹄动物" << endl;
		break;
	}
	case 29: {
		cout << "根据给出条件,所查询出结果为:老虎" << endl;
		break;
	}
	case 30: {
		cout << "根据给出条件,所查询出结果为:金钱豹" << endl;
		break;
	}
	case 31: {
		cout << "根据给出条件,所查询出结果为:长颈鹿" << endl;
		break;
	}
	case 32: {
		cout << "根据给出条件,所查询出结果为:斑马" << endl;
		break;
	}
	case 33: {
		cout << "根据给出条件,所查询出结果为:鸵鸟" << endl;
		break;
	}
	case 34: {
		cout << "根据给出条件,所查询出结果为:企鹅" << endl;
		break;
	}
	case 35: {
		cout << "根据给出条件,所查询出结果为:海燕" << endl;
		break;
	}
	}
}

void main() {
aceu:
	cout << "********产生式系统推理——动物识别系统*********" << endl;
	//输出序号以及所有条件图
	cout << "1.有奶          2.有毛发      3.有羽毛        4.会飞" << endl;
	cout << "5.能生蛋        6.有爪        7.有犬齿        8.目盯前方" << endl;
	cout << "9.吃肉          10.有蹄       11.反刍         12.黄褐色" << endl;
	cout << "13.黑色斑点     14.长腿       15.长脖子       16.暗斑点" << endl;
	cout << "17.黑色条纹     18.白色       19.不会飞       20.黑白色" << endl;
	cout << "21.会游泳       22.善飞       23.不怕风浪" << endl<<endl;
	cout << "请输入查询动物的特征序号,以‘0’结束" << endl;
	//测试创建指针数组
	int test[20] = { 0 };
	int q = 1;
	for (int i = 0; q != 0; i++) {
		cin >> test[i];
		q = test[i];
	}
	//读数,并写入ch数组
	int num = 0;
	while (test[num] != 0)
		num++;
	int* ch = new int[num];
	for (int i = 0; i < num; i++)
		ch[i] = test[i];
	show(result(ch, rule3(ch, rule2(ch, rule1(ch)))));
	int re;
	cout <<endl<<endl<<"******************************************************"<<endl<< "请输入一个数以便继续,输入0则退出" << endl;
	cin >> re;
	if (re != 0) {
		system("cls");
		goto aceu;
	}
}
  • 7
    点赞
  • 107
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值