产生式正向推理和逆向推理程序

        最近人工智能课程结束,本来是考试课,但老师说不用考试,交个设计就行。我就喜欢这样的老师,为我老师点赞,顺便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 

 

 

 

 

 

 

 

 

 

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页