人工智能基础-生产式系统

//植物类别,植物个体
//判断是否是植物
//菜单函数(可无)
//去除已经出现的父亲节点的子节点特征
//正向推理函数
//输出植物的类别和个体
//函数:计算置信度
//函数:重新计算置信度
//函数:反向推理
//函数:用户输入特征
//函数:置信度比较
//函数:置信度排序
/*植物     界                                                         科                           形态特征
樱桃:  被子植物门:(具有多数雄蕊)              蔷薇科:       红色核果近球形		
荷花:  被子植物门:(水生+花具有离生心皮)  睡莲科:      花单生于花梗顶端
松树:  裸子植物门:(常绿乔木+仅有长枝)     松科:          叶成针状
银杏:  裸子植物门:(叶扇形)                        银杏科:       叶落前变为黄色
竹子:  被子植物门:(草质+短根茎)               禾本科:       叶呈狭披针形+生长迅速
白菜:  被子植物门:(十字形花冠)                 十字花科:    二年生草本植物
芦苇:  被子植物门:(草质+短根茎)               禾本科:       繁殖能力极强易形成群落
蒲公英:被子植物门:(有乳汁管+花左右对称) 菊科:          根头部有白色毛茸
*/


/*规则库:
R1:种子有果皮->被子植物
R2:种子无果皮->裸子植物
R3:被子植物&有托叶->蔷薇科
R4:被子植物&水生&花具有离生心皮->睡莲科
R5:裸子植物&常绿乔木&仅有长枝->松科
R6:裸子植物&叶扇形->银杏科
R7:被子植物&草质&短根茎->禾本科
R8:被子植物&十字形花冠->十字花科
R9:被子植物&有乳汁管&花左右对称->菊科
R10:蔷薇科&红色核果近球形->樱桃
R11:睡莲科&花单生于花梗顶端->荷花
R12:松科&叶成针状->松树
R13:银杏科&落叶前变为黄色->银杏
R14:禾本科&叶成狭披针形&生长迅速->竹子
R15:十字花科&二年生草本植物->白菜
R16:禾本科&繁殖能力强易形成群落->芦苇
R17:菊科&根头部有白色毛绒->蒲公英
*/

设在综合数据库中存放有下列已知事实:
该植物特征:种子有果皮,草质,短根茎,叶成狭披针形,生长迅速
当推理开始时,推理机构的工作过程是:
①从规则库中取出第一条规则R1,检查其前提是否可与综合数据库中的已知事实匹配成功。R1的前提“种子有果皮”可与综合数据库中的已知事实“种子有果皮”匹配。再检查R2至R17结果均不能匹配,因为只有R1一条规则被匹配,所以R1被执行,并将其结论部分“被子植物”加入到综合数据库中。并且将R1标注已经被选用过的记号,避免下次再被匹配。
此时综合数据库的内容变为:
该植物特征为:种子有果皮,草质,短根茎,叶成狭披针形,生长迅速,被子植物
检查中和数据库中的内容,没有发现要识别的任何一种植物,所以要继续进行推理
②分别用R1,R2,R3,R4,R5,R6与综合数据库中的已知事实进行匹配,均不成功。但当用R7于之匹配时,获得了成功。再检查R8至R17均不能匹配。因为只有R7一条规则被匹配,所以执行R7并将其结论部分“禾本科”加入到综合数据库中,并将R7标注已经被选用过的记号,避免下次再被匹配。
此时综合数据库的内容变为:
该植物特征为:种子有果皮,草质,短根茎,叶成狭披针形,生长迅速,被子植物,禾本科
检查中和数据库中的内容,没有发现要识别的任何一种植物,所以要继续进行推理
③在此之后,除已经匹配过的R1,R7外,只有R14可与综合数据库中的已知事实匹配成功,所以将R14的结论加入综合数据库,此时综合数据库的内容变为:
该植物特征为:种子有果皮,草质,短根茎,叶成狭披针形,生长迅速,被子植物,禾本科,竹子
检查综合数据库中的内容,发现要识别的植物竹子包含在了综合数据库中,所以推出了“该植物是竹子”这一最终结论。至此,问题的纠结过程就结束了。

#include<iostream>
#include<iomanip>
#include<string>
#include<stdlib.h>
#include<algorithm> 
#include<vector>
using namespace std;

//数据库内的植物的名称
string plant[]={"樱桃","荷花","松树","银杏","竹子","白菜","芦苇","蒲公英"};

//植物的特征
string feature[]={
"红色核果近球形","花单生于花梗顶端","叶成针状","落叶前变为黄色","叶成狭披针形","生长迅速","二年生草本植物","繁殖能力强易形成群落","根头部有白色毛绒",   //区别个体的特征
//0                                  1                           2                  3                        4                   5                    6                               7                                8        

"种子有果皮","种子无果皮","有托叶","水生","花具有离生心皮","常绿乔木","仅有长枝","叶扇形","草质","短根茎","十字形花冠","有乳汁管","花左右对称",//区别科目的特征
//  9                    10              11         12              13                  14             15           16         17        18              19               20              21 

"被子植物", "裸子植物", "蔷薇科", "睡莲科", "松科", "银杏科", "禾本科", "十字花科", "菊科", //科目
//   22            23              24            25        26          27           28             29          30 

"樱桃", "荷花", "松树", "银杏", "竹子", "白菜", "芦苇", "蒲公英" };//个体
// 31       32      33        34       35        36        37        38


//存放规则的结构体 
typedef struct 
{  
	int relation[7];   //关系 
	int name;		   //推理结果  
}Rule;// 存放可能的植物 

typedef struct
{
	int plant;         // name 
	double confidence; //置信度 = 满足的特性数 / 所含特征数;
	int site;          // 在rule中的位置
	int num;           // 满足的特征数 
	int size;          // 此animal的所含总特征数 
}Result;

vector<Result> result;
//规则库
//输入规则时最后输入-1则代表规则结束 
Rule rule[17] = 
{
{ { 9, -1 }, 22 },
// R1:种子有果皮->被子植物
{ { 10, -1 }, 23 },
// R2:种子无果皮->裸子植物
{ { 22, 11, -1 }, 24 },
// R3:被子植物&有托叶->蔷薇科
{ { 22, 12, 13, -1 }, 24 },
// R4:被子植物&水生&花具有离生心皮->睡莲科
{ { 23, 14, 15, -1 }, 26 },
// R5:裸子植物&常绿乔木&仅有长枝->松科
{ { 23, 16, -1 }, 27 },
// R6:裸子植物&叶扇形->银杏科
{ { 22, 17, 18, -1 }, 28 },
// R7:被子植物&草质&短根茎->禾本科
{ { 22, 19, -1 }, 29 },
// R8:被子植物&十字形花冠->十字花科
{ { 22, 20, 21, -1 }, 30 },
// R9:被子植物&有乳汁管&花左右对称->菊科
{ { 24, 0, -1 }, 31 },
// R10:蔷薇科&红色核果近球形->樱桃
{ { 25, 1, -1 }, 32 },
// R11:睡莲科&花单生于花梗顶端->荷花
{ { 26, 2, -1 }, 33 },
// R12:松科&叶成针状->松树
{ { 27, 3, -1 }, 34 },
// R13:银杏科&落叶前变为黄色->银杏
{ { 28, 4, 5, -1 }, 35 },
// R14:禾本科&叶成狭披针形&生长迅速->竹子
{ { 29, 6, -1 }, 36 } 
// R15:十字花科&二年生草本植物->白菜
{ { 28, 7, -1 }, 37 },
// R16:禾本科&繁殖能力强易形成群落->芦苇
{ { 30, 8, -1 }, 38 } 
// R17:菊科&根头部有白色毛绒->蒲公英
};


int flag[30] = { 0 };  //标记各个特征是否选择
int IsPlant(int a);
int change_speices();  //将可以推理出植物类的规则进行 
int fnum();            //获取flag标记的数目 
int z_inference();     //正向推理 
int category();        //输出植物类别 
void cal_confi();      //计算置信度 
void r_inference();    //反向推理 
void input();          //输入 
void menu();           //菜单 

//判断置信度大小
bool Compare(const Result& a, const Result& b)
{
	return a.confidence > b.confidence;
}

//排序并返回排序结果
void Rsort(vector<Result>& r)
{
	//调用数组容器的排序函数
	sort(r.begin(), r.end(), Compare);
	return;
}

//选择特征菜单 
void menu()
{
	//输出知识库中特征数组除植物名的成员,每输出4个换行
	for (int i = 0; i < sizeof(feature) / sizeof(feature[0]) - sizeof(plant) / sizeof(plant[0]); i++)
	{
		if (i % 4 == 0 && i != 0)
		{
			cout << endl;
		}
		cout << setiosflags(ios::left) << setw(2) << i << ".";
		cout << setiosflags(ios::left) << setw(15) << feature[i];
	}
	memset(flag, 0, sizeof(flag));//初始化标记数组为0
}

//特征输入值选择数字 
void input()
{
	for (int i = 0; i < sizeof(feature) / sizeof(feature[0]) - sizeof(plant) / sizeof(plant[0]); i++)
	{
		flag[i] = 0;
	 }
	int  key = 0;
	cout << endl << "输入所选植物特征:";
	while (key != -1)//当输入-1时停止特征输入
	{
		cin >> key;
		if (key >= 0 && key <= 30)
		{
			flag[key] = 1;
		}
		else if (key != -1)
		{
			cout << "输入错误,请输入一个特征:" << endl;
			cin.clear();		//清除流错误错误标记
			cin.sync(); 		//清空输入缓冲区
			cout << "请继续输入:";
		}
	}
}


//是某植物而不是某种物种 
int IsPlant(int a)
{
	if (a >= sizeof(feature) / sizeof(feature[0]) - sizeof(plant) / sizeof(plant[0]) && a <= sizeof(feature) / sizeof(feature[0]))
	{
		return 1;
	}
	return 0;
}

//判断是否某一物种类 
int IsPlant_speices(int a)
{
	if (a >= 22 && a <= 30)
	{
		return 1;
	}
	return 0;
}

//返回flag数组中标记的总数 
int fnum()
{
	int fum = 0;
	for (int i = 0; i < 30; i++)
	if (flag[i] == 1)
	{
		fum++;
	}
	return fum;
}

//输出打印物种类别
int category()
{
	bool k;
	int count = 0;
	for (int i = 18; i < 30; i++)
	{
		k = false;
		if (flag[i] == 1)
		{
			k = true;
			count++;
			if (count == 1)
			{
				cout << "无法识别植物! 所属类为:";
			}
			cout << setiosflags(ios::left) << setw(10) << feature[i];
		}
	}
	cout << endl;
	if (!k)
	{
		cout << "系统无该植物" << endl;
	}
	return 1;
}

//改变特征值,变化flag,推理是否有物种种类,并将用到的事实清空,改flag为0
int change_speices()
{
	int i, j, key;
	bool t;
	int temp[30] = { 0 }; //临时存储,方便修改
	int f[30] = { 0 }; // 标记使用过的flag 
	for (i = 0; i < 9; i++)
	{ 
		//前9个规则
		t = true;
		j = 0;
		key = rule[i].relation[j];
		while (key != -1)//遍历该条关系
		{
			if (flag[key] == 1)
			{
				temp[key] = 1;
			}
			else 
			{
				memset(temp, 0, sizeof(temp));
				t = false;
				break;
			}
			j++;
			key = rule[i].relation[j];
		}
		if (t)
		{
			for (int k = 0; k <= 21; k++)
			{
				if (temp[k] == 1)
				{
					f[k] = 1;
				}
			}
			flag[rule[i].name] = 1;
		}
		memset(temp, 0, sizeof(temp));
	}
	//删除推理过的事实,保留结果 
	for (i = 0; i <= 21; i++)
	{
		if (f[i] == 1)
		{
			flag[i] = 0;
		}
	}
	return 1;
}

//重新计算置信度 
void cal_confi()
{
	for (int i = 0; i < result.size(); i++)
	{
		for (int j = 9; j < 17; j++)
		{
			if (result[i].plant == rule[j].name)
			{
				result[i].confidence = 1.0 * result[i].num / result[i].size;
				break;
			}
		}
	}
}

//双向推理,正向推理不下去,事实不够,采用逆向推理
int z_inference()
{
	int key, num;
	int i, j;
	int fum = fnum();
	cout << endl;
	for (i = 9; i < 17; i++)
	{  
		//检查规则库
		Result temp;
		j = 0; num = 0;
		key = rule[i].relation[j];
		while (key != -1)
		{
			if (flag[key] == 1)
			{
				num++;
			}
			j++;
			key = rule[i].relation[j];
		}
		//此时j保存则rule[i]所含有的特征数 
		if (num != 0 && fum <= j)
		{  
			//给定特征数小于等于的情况,即flag数组中标记位数目大于此植物的特征数则不放入result
			if (IsPlant(rule[i].name))
			{ 
				// 是具体的植物 
				temp.plant = rule[i].name;
				int size = j; //rule[i]所含有的特征数
				temp.size = size;
				temp.confidence = 1.0 * num / size;
				temp.site = i;
				temp.num = num;
				result.push_back(temp);
			}
		}
	}
	if (!result.empty())
	{
		Rsort(result);  //对置信度从高到低排序 
	}
	//正向推理后 
	if (result.empty()) 
	{ 
		//给定特征数无法用任何一规则推理,可能没有这种植物,可能是一种类别 
		category();
	}
	else if (result.front().confidence == 1.0)
	{ 
		//可能给的特征刚好推理出,可能特征还没用完
		cout << "植物为:" << feature[result.front().plant] << endl;
		result.clear();//清空
		return 1;
	}
	else//逆向推理,询问特征
	{
		r_inference();
	}
}


//特征不足,进入反向推理 
void r_inference()
{
	vector<Result>::iterator it = result.begin();
	int enquire[30];  
	//用来标记询问过的特征数组(0代表没有此特征,1代表有,2代表不请楚、	不知道) 
	memset(enquire, -1, sizeof(enquire));
	for (int i = 0; i< result.size();)
	{
		//从置信度最高开始询问
		bool in_i = true; // i ++ 的标记 
		int  nu = result[i].size;
		for (int j = 0; j < nu; j++)
		{  
			//询问特征 
			if (flag[rule[result[i].site].relation[j]] == 0)
			{
				int en = rule[result[i].site].relation[j];
				char c;
				if (enquire[en] == -1)
				{ 
					//此特征未被询问过,则输出询问语句,否则直接判断处理 
					cout << "是否有以下特征:" << feature[rule[result[i].site].relation[j]] << endl;
					cout << "Y(y) or N(n) or D(don't know) : ";
					cin >> c;
					while (c != 'Y' && c != 'y' && c != 'N' && c != 'n' && c != 'D' && c != 'd')
					{
						cout << "输出选择:Y(y) N(n) D(d)!" << endl;
						cin >> c;
					}
				}
				if (enquire[en] == 1 || c == 'Y' || c == 'y')
				{
					//有此特征  改变置信度
					result[i].num++;
					enquire[en] = 1;
				}
				else if (enquire[en] == 0 || c == 'N' || c == 'n')
				{ 
					// 没有此特征  直接去掉
					enquire[en] = 0;
					result.erase(it + i);
					//erase删除后i不自增,就能删除最后的元素,迭代器指向删除之前元素后的第一个元素
					in_i = false; //如果擦除了元素,则i不自增 
					if (result.empty())//result为空,输出类别后退出 
					{
						category();
					}
					break;
				}
				else if (enquire[en] == 2 || c == 'D' || c == 'd')
				{
					enquire[en] = 2;  // 不确定、不知道,则置信度不改变 
				} 	
			}
		}
		if (in_i)
		{
			++i;
		}
	}

	if (!result.empty())
	{
		// 改变置信度 
		cal_confi();
		if (result.size() > 1) //重新排序
		{
			Rsort(result);
		}
		//判断询问后进行双向推理
		if (result.front().confidence == 1.0)
		{
			cout << "植物是:" << feature[result.front().plant] << endl;
		}
		else
		{
			cout << "植物可能为(置信度从高到低):";
			for (vector<Result>::iterator it = result.begin(); it != result.end(); ++it)
			{
				cout << setiosflags(ios::left) << setw(10) << feature[(*it).plant] << " ";
			}
			cout << endl;
		}
		result.clear(); // 清空 
	}
}


int main()
{
	char q;
	cout << "是否进入系统:Y(y) N(n)";
	cin >> q;
	while (q != 'N' && q != 'n')
	{
		menu();
		input();
		change_speices();
		z_inference();
		cout << endl << "继续?(Y/N)" << endl;
		cin >> q;
		system("cls");//清屏进行新的操作
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值