最近人工智能课程结束,本来是考试课,但老师说不用考试,交个设计就行。我就喜欢这样的老师,为我老师点赞,顺便diss一下上课念ppt,停电就没法上课,考试靠死记硬背的某些老师。扯得有点远了,先说说产生式的存储,我是用c#语言中动态数组ArrayList存储的:
//规则中的每个子条件及结论都是一个结构体类型:
private struct Pre
{
public int Num; //规则号
public char Element; //规则中的子条件的项
public float Uncertain; //子条件的不确定性
public bool Logic; //与true; 或false
}Pre pre;
//将结构体变量pre作为数组中的一项分别存入不同的动态数组ArrayList中:
private ArrayList Prerule = new ArrayList(); //规则的前提
private ArrayList Conrule = new ArrayList(); //规则的结论
private ArrayList Rule = new ArrayList(); //规则文字叙述
private ArrayList SynDB = new ArrayList(); //综合数据库
private ArrayList SynDB2 = new ArrayList(); //正向推理时,需要往该数组中添加论据。当修改待论证论据时,将SynDB赋值到SynDB2中,以返回初始状态
private static int Numrule = 0; //记录规则的序号,初始为 0
private int[] Numele = new int[20]; //用来记录每条规则元素的个数
private int[] Numele2 = new int[20]; //正向推理时,会修改该数组的内容。当修改待论证论据时,将Numele赋值到Numele2中,以恢复初始状态
private Queue q = new Queue(); //用队列作为逆向推理时的假设集
-
正向推理
正向推理是一种从已知事实出发、正向使用推理规则的推理方式,亦称为数据驱动推理或前项链推理。
正向推理过程可用如下算法描述:
(1)把用户提供的初始证据放入综合数据库。
(2)检查综合数据库中是否包含了问题的解,若已包含,则求解结束,并成功退出;否则执行下一步。
(3)检查知识库中是否有可用知识,若有,形成当前可用知识集,执行下一步;否则转(5)。
(4)按照某种冲突消解策略,从当前可用知识集中选出一条知识进行推理,并将推出的新事实加入综合数据库中,然后转(2)。
(5)询问用户是否可以进一步补充新的事实,若可补充,则将补充的新事实加入综合数据库中,然后转(3);否则表示无解,失败退出。
private void button5_Click(object sender, EventArgs e) //正向推理
{
richTextBox1.Text = ""; //清屏 结果框
//SynDB2 = SynDB; //数组是引用类型,这样赋值是错误的,修改一个也会改变另一个
SynDB2.Clear(); //将SynDB数组中内容复制到SynDB2中,每次正向推理SynDB2都要恢复初始状态
for (int i = 0; i < SynDB.Count; i++)
{
char str = (char)SynDB[i];
SynDB2.Add(str);
}
Array.Copy(Numele, Numele2, Numele2.Length); //将Numele数组内容复制给Numele2,在每次正向推理时返回初始状态
char Endargue = Convert.ToChar(textBox4.Text); //待论证的结论
int flag = 0; //用来记录调用select()函数的次数
while (!SynDB2.Contains(Endargue) && flag < Numrule) //若综合数据库中已有问题的解,直接退出
{
select();
flag ++;
}
if (flag <= Numrule) //在调用select()函数次数小于规则总数,就推出论,!SynDB2.Contains(Endargue)为假退出循环
richTextBox1.Text += "推理成功";
else //在调用select()函数次数等于规则总数,仍没有推出结论,flag < Numrule为假退出循环
richTextBox1.Text += "推理失败";
}
private void select() //根据已给的论据,推出新知识,并加入综合数据库
{
//计算每条规则的子条件的已知数,和其总数比较。若相等,则该规则可用,并将其结论加入SynDB动态数组中
int[] mark = new int[20]; //用来记录每条规则的有多少子条件已知
for (int i = 0; i < SynDB2.Count; i++)//在综合数据库中依次选取元素进行比较
{
char str = (char)SynDB2[i];
for (int j = 0; j < Prerule.Count; j++)
{
Pre pre2 = (Pre)Prerule[j];
if (pre2.Element == str) //若相等,该条规则加 1
mark[pre2.Num] += 1;
}
}
//与记录每条规则有多少子条件的数组Numele2[]比较
for (int i = 0; i < 10; i++)
{
if (mark[i] >= Numele2[i] && mark[i] >0) //mark[i] > 0,是因为数组默认初始值为0,两个数组存在很多 0 = 0 的情况
{
Pre pre2 = (Pre)Conrule[i-1];
SynDB2.Add(pre2.Element); //就将该条规则的结论,加入到综合数据库中,即SynDB动态数组中
Numele2[i] = 100; //为了让该规则下次循环不满足条件,将其设为大正数
richTextBox1.Text += Rule[i - 1].ToString()+"\r\n";
break; //一次只触发一条规则
}
}
}
-
逆向推理
(1)将要求证的目标(称为假设)构成一个假设集;
(2)从假设集中选出一个假设,检查该假设是否在综合数据库中,若在,则该假设成立,此时,若假设集为空,则成功退出,否则仍执行(2);若该假设不在数据库中,则执行下一步;
(3)检查该假设是否可由知识库的某个知识导出。若不能由某个知识导出,则询问用户该假设是否为可由用户证实的原始事实,若是,该假设成立,并将其放入综合数据库,再重新寻找新的假设,若不是,则转(5);若能由某个知识导出,则执行下一步;
(4)将知识库中可以导出该假设的所有知识构成一个可用知识集;
(5)检查可用知识集是否为空,若空,失败退出;否则执行下一步;
(6)按冲突消解策略从可用知识集中取出一个知识,继续执行下一步;
(7)将该知识的前提中的每个子条件都作为新的假设放入假设集,转(2)。
private void button6_Click(object sender, EventArgs e) //逆向推理
{
richTextBox1.Text = ""; //清屏 结果框
char Endargue = Convert.ToChar(textBox4.Text); //待论证的结论
//将待论证的结论入队,队的元素是Pre类型的,所以要为其添加字段
pre.Element = Endargue; pre.Num = 0; pre.Uncertain = 1; pre.Logic = true;
q.Enqueue(pre); //入队列
while (q.Count > 0) //但队为空时,退出循环
{
Pre pre2 = new Pre();
pre2 = (Pre)q.Dequeue(); //出队
if (SynDB.Contains(pre2.Element)) //若该假设在综合数据库asynDB中,若在,则该假设成功
{
richTextBox1.Text += "已知条件中已存在" + pre2.Element.ToString()+"\r\n";
if (q.Count == 0) //此时假设集若为空,即队列为空,推理成功
{
richTextBox1.Text += "推理成功\r\n";
break;
}
}
else
{
if (Conrule.Contains(pre2) || exist_conrule(pre2)) //如果假设在结论conrule动态数组中,则将假设的所有子条件入队。
{
richTextBox1.Text += "证明" + pre2.Element.ToString() + ",需证明: ";
for (int i = 0; i < Conrule.Count; i++)
{
Pre pre3 = (Pre)Conrule[i];
if (pre2.Element == pre3.Element) //假设和结论相同
{
richTextBox1.Text += Rule[i].ToString();
//将 pre3.Num或i+1 号规则的前提入队
for (int j = 0; j < Prerule.Count; j++)
{
pre = (Pre)Prerule[j];
if (pre.Num == pre3.Num)
q.Enqueue(pre); //入队
}
}
//richTextBox1.Text += "\n";
}
}
else //不在conrule动态数组中,则不可由规则推导出来,即无解
{
richTextBox1.Text += pre2.Element.ToString() + " 无解\r\n";
if (q.Count > 0 && pre2.Logic) //如果假设集不为空,且规则为与, 则要将规则的前提子条件全部从队列中剔除。若为或,只需剔除该假设
{
Delete_queue(pre2.Num);
}
if (q.Count == 0) //假设集为空,此时推理失败
richTextBox1.Text += "推理失败";
}
}
}
}
private void Delete_queue(int number) //在队列中剔除该规则所有的子条件
{
Pre pre2 = new Pre();
for (int i = 0; i < Prerule.Count; i++)
{
pre = (Pre)Prerule[i];
if (pre.Num == number && q.Contains(pre))
pre2 =(Pre)q.Dequeue(); //因为一个规则的前提是按前后顺序存入的,因此当一个子条件无解时,该规则的其他子条件定也紧随其后
}
}
//写这个函数,主要是为了第一个假设入队时,只有DElement属性,其他属性Num,Logic,Uncertain都不存在,不能直接使用这个 Conrule.Contains(pre2)函数
private bool exist_conrule(Pre pre2)
{
Pre pre3 = new Pre();
for (int i = 0; i < Conrule.Count; i++)
{
pre3 = (Pre)Conrule[i];
if (pre2.Element == pre3.Element)
return true;
}
return false;
}
-
运行结果
完整的项目源代码已上传,用vs就可直接运行,有需要的可以点击链接下载。以上代码都是小弟一个人瞎琢磨的,欢迎大神指点。 https://download.csdn.net/download/langwen2048/10751260 CSDN变了,把我的下载链接到广告了,时隔一年不得不重新找到资源在百度云上传。下载链接: https://pan.baidu.com/s/1aFK-iwinygfAuf-5S9UxDw 提取码: wpnr