人工智能实验报告二班级:XXXX 姓名:XXXX 学号:XXXXX 一.实验目的1. 理解并掌握基于规则系统的表示与推理。
2. 学会编写小型的产生式系统,理解正向推理和反向推理的过3. 程以及两者的差别。
4. 学会设计简单的人机交互界面。
二.实验预习内容及实验环境:1. 了解动物识别系统问题;2. 产生式系统的组成部分,正向推理,逆向推理的算法和产生式系统的类型。
三、实验内容和步骤:1.内容:结合第五章内容,以动物识别系统(或货物袋装系统)为例,实现基于规则的系统构造实验。
2.要求:1>结果显示要求(1)有若干选择动物特征的选择列表;(2)表现判断动物时,使用了哪些规则;(3)表现数据库的变化;(正向推理中表现每使用一次规则后,产生新事实。
反向推理中表现新的目标事实有哪些需要证明,哪些已经得到证实);(4)显示规则的调用次序;(5)显示最后的结果,包含动物能识别出来和动物不能识别出来两种情况。
(6)至少检查两个例子实现正向推理和反向推理的区别;老虎的例子如上所示,以下为金钱豹的例子:正向推理:反向推理:2>指出综合数据库和规则库分别使用哪些函数实现的?综合数据库和规则库均采用数组完成。
其中的综合数据库为一个string类型的数组,内容为事实的名称字符串,下标则为该事实的序号。
数组长度为33,表示共有33个事实(最后7个为动物名称)。
代码如下:string facts[factnum]={"有爪","有犬齿","有奶","有毛发","目视前方","吃肉","有蹄","有羽毛","会飞","生蛋","不会飞","黑白色","会游泳","善飞","不怕风浪","长腿","长脖子","有暗斑点","白色","黑色斑点","黑色条纹","黄褐色","食肉动物","鸟","哺乳动物","有蹄动物","老虎","金钱豹","长颈鹿","斑马","鸵鸟","企鹅","海燕"};规则库为一个结构体数组。
该结构体由一个int型数组(表示前提条件的序号集合)和一个int数据(表示由此前提推出的结果)表示。
该数组长度为14,表明有14条规则。
该规则库在建立时有一个内在要求:前提的规则(子规则)的序号必须在父规则之前。
(便于正向推理的实现)代码如下:struct rule{int con[10];int res;};Rulerules[rulenum]={{{2,-1},24},{{3,-1},24},{{7,-1},23},{{8,9,-1},23},{{24,0,1,4,-1},22 }, {{24,5,-1},22},{{24,6,-1},25},{{22,21,20,-1},26},{{22,21,19,-1},27},{{25,15,16,21,17,-1},28},{{25,18,20,-1},29},{{23,10,15,16,11,-1},30},{{23,10,12,11,-1},31},{{23,13,14,-1},32}};3>规则库的匹配算法是什么?如何选用可用规则集中的规则?分别使用哪些函数实现的?用一个bool型的与数据库数组等长的enfact数组表示各事实是否已经满足(或已经推出)。
要匹配一个规则的时候,只需查看其前提序号集数组中每个元素对应的enfact数组中的值是否为true,如果所有都为true则可由该规则推出相应结果。
代码如下:bool enfact[factnum];memset(enfact,false,factnum);//每次输入时需对该数组进行初始化//以下为输入过程while(ti!=-1){if(ti>=0&&ti<=notanimal)enfact[ti]=true;else{cout<<"输入错误!请输入0~25之间的数字!"<< endl;//notanimal=25cin.clear();//清除流错误错误标记cin.sync(); ////////////清空输入缓冲区break;}cin>> ti;};以上,则完成了输入,并对enfact数组进行了初始化。
现在对正向推理和反向推理的匹配和具体推理过程进行简要说明。
正向推理:从下向上的推理。
由于建立规则库时的内在要求,即子规则必在父规则前,故进行正向推理的时候只要将规则库从前到后扫一遍看是否能由规则推出相应结果即可。
如果能匹配推出结果则看该结果是否为动物,如果已经推出动物则推理成功。
否则更新数据库,匹配下一个规则。
代码如下:int obverse(){outputf(1);int ti;int i,j;int tres;cout<<endl;for(i=0;i<rulenum;i++){j=0;ti=rules[i].con[j];while(ti!=-1) //假设前提数不超过9个,必存在-1作为结束{if(enfact[ti]==false)break;j++;ti=rules[i].con[j];}if(ti==-1){tres=rules[i].res;enfact[tres]=true;cout<<"运用了规则"<<i<<" : ";j=0;while(rules[i].con[j]!=-1){cout<<facts[rules[i].con[j]]<<" ";j++;}cout<<"====> "<<facts[tres]<<endl;if(isAnimal(tres)){if(noOtiose(tres))return tres;elsereturn -1;}}}return -1;}反向推理:从上向下的推理。
反向推理比正向推理要复杂一些。
采用的算法是从事实数据库的动物开始从前往后进行匹配,看是否能成功推出,如果都推不出能识别失败,若能推出其中一个则中止搜索,识别成功。
推某一个事实时,仍然是从该事实的前提出发,逐个匹配,若所有的前提满足,则该事实满足。
不同的是,这里以该事实为结果的前提可能有很多,我采用了牺牲空间换时间的方法,即不是将规则库从头到尾搜一遍来查找以待查事实为结果的规则,而是事先将所有的先保存在一个结构体数组中,结构体中只有一个数组,表示能推出某个结果的规则序号。
代码如下:struct factrule{int rulen[5];};factrulefactrules[factnum]={{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{-1},{4,5,-1},{2,3,-1},{0,1,-1},{6,-1},{7,-1},{8,-1},{9,-1},{10,-1},{11,-1},{12,-1}};故要看某个事实是否能推出的时候,只要将它对应的标号的factrules中的数组中的规则进行匹配即可,只要有一条规则满足,即可中止搜索,推出该事实可满足。
另外,由于某个规则的前提可能是另一个规则的结果,这相当于规则的嵌套。
所以在进行反向推理时必须用到递归技术,并且是一种回溯。
整个反向推理的代码如下:bool achieve(int ri) //{int j;int ti;cout<<endl<<"当前验证目标为: "<<facts[rules[ri].res]<<endl;cout<<"尝试规则"<<ri<<" : ";j=0;while(rules[ri].con[j]!=-1){cout<<facts[rules[ri].con[j]]<<" ";j++;}cout<<"==?==> "<<facts[rules[ri].res]<<endl;j=0;ti=rules[ri].con[j];while(ti!=-1){if(enfact[ti]==false){int ki=0;if(factrules[ti].rulen[ki]==-1){cout<<"条件"<<facts[ti]<<"不满足!"<<endl;cout<<"使用规则"<<ri<<" : ";j=0;while(rules[ri].con[j]!=-1){cout<<facts[rules[ri].con[j]]<<" ";j++;}cout<<"=====> "<<facts[rules[ri].res]<<"失败!"<<endl;return false;}else{cout<<"条件"<<facts[ti]<<"未知,继续推理..."<<endl;bool ok=false;for(ki=0;factrules[ti].rulen[ki]!=-1&&!ok;ki++){ok=achieve(factrules[ti].rulen[ki]);}if(ok){cout<<"条件"<<facts[ti]<<"满足!"<<endl;j++;ti=rules[ri].con[j];}else{cout<<"条件"<<facts[ti]<<"不满足!"<<endl;cout<<"使用规则"<<ri<<" : ";j=0;while(rules[ri].con[j]!=-1){cout<<facts[rules[ri].con[j]]<<" ";j++;}cout<<"=====> "<<facts[rules[ri].res]<<" 失败!"<<endl<<endl;return false;}}}else{cout<<"条件"<<facts[ti]<<"满足!"<<endl;j++;ti=rules[ri].con[j];}}{cout<<"使用规则"<<ri<<" : ";j=0;while(rules[ri].con[j]!=-1){cout<<facts[rules[ri].con[j]]<<" ";j++;}cout<<"=====> "<<facts[rules[ri].res]<<"成功!"<<endl;return true;}}int reverse(){outputf(2);int i;for(i=animalfrom;i<rulenum;i++){cout<<endl<<"----------------------由动物目标:"<<facts[rules[i].res]<<"进行反向推理-------------------------"<<endl;if(achieve(i)){if(noOtiose(i+notanimal-animalfrom+1))return i+notanimal-animalfrom+1;elsereturn -1;}}return -1;}注:本系统对输入事实冗余的情况做出了处理。