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