人工智能---动物识别系统

人工智能动物识别系统规则库的实现

0 前言

       由于研究生阶段的需要,需要系统学习C#语言,为了将最近学习的东西进行一个总结,偶然间想到了本科写的那个动物识别系统,用C语言写的很简单,只是用了简单的if…else语句,这里也是简单的用C#语言进行了一个复现。原来的C语言版本在这里:动物识别系统C语言版

1 问题重述和技术背景

1.1 问题重述

       有如下规则库,用户来选择这些条件,根据用户输入的条件来判断这是一种什么动物:

编号 IF条件 推论
R1 有毛 哺乳动物
R2 有奶 哺乳动物
R3 有羽毛
R4 会飞 & 会下蛋
R5 吃肉 食肉动物
R6 有犬齿 & 有爪 & 眼盯前方 食肉动物
R7 哺乳动物 & 有蹄 有蹄类动物
R8 哺乳动物 & 反刍动物 有蹄类动物
R9 哺乳动物 & 食肉动物 & 黄褐色 & 暗斑点 金钱豹
R10 哺乳动物 & 食肉动物 & 黄褐色 & 黑色条纹
R11 有蹄类动物 & 长脖子 & 长腿 & 暗斑点 长颈鹿
R12 有蹄类动物 & 黑色条纹 斑马
R13 鸟 & 不会飞 & 长脖子 & 长腿 & 黑白二色 鸵鸟
R14 鸟 & 不会飞 & 会游泳 & 黑白二色 企鹅
R15 鸟 & 善飞 信天翁

       如果用一个图来表示就是这样的:
人工智能---动物识别系统_第1张图片
图中,食肉动物那一列,需要同时满足时哺乳动物才能继续往下推出时金钱豹或老虎。

1.2技术背景

        我采用的是字典作为知识库和规则库的存储结构,知识库的结构是:

Dictionary<int, string> _knowledgeBase = new Dictionary<int, string>()
{
   {1, "有毛发"},{2, "有奶 "},{3 ,"有羽毛"},{4 ,"会飞 "},
   {5 ,"会下蛋"},{6 ,"眼盯前方"},{7 ,"有蹄 "},{8 ,"反刍动物"},
   {9 ,"吃肉 "},{10 ,"有犬齿"},{11 ,"有爪"},{12 ,"长脖子"},
   {13 ,"长腿"},{14 ,"有暗斑点"},{15 ,"有黑色条纹"},{16 ,"黄褐色"},
   {17 ,"不会飞"},{18 ,"黑白二色"},{19 ,"会游泳"},{20 ,"擅飞"}
};

这样知识库条件的序号就一一对应上了,也方便输出。

规则库的结构是:

Dictionary<int[], string> _ruleBase = new Dictionary<int[], string>();

之所以选择int数组作为其存储结构,在用户输入后,把用户输入的条件序号转换为一个int数组,这样就可以直接看字典中是否有这个键,当然要对这个int数组进行一个约束,因为对于判断数组相同的要求是:1.数组元素个数相同。2.对应位置上的元素相同,我这里采用的方式是对int数组进行一个排序,即使用户不是按照顺序输入的条件,也能进行判断。
        遇到的问题:因为int数组是引用类型,存储的是地址,因次即使Dictionary中存在和用户输入相同的键,也不能进行判断,用户输入的条件数组的地址和Dictionary中键的地址是不一样的,比如:

 Dictionary<int[], string> _rule = new Dictionary<int[], string>();
 _rule.Add(new int[] { 1, 2, 3 }, "鸟");
 int[] rule = { 1, 2, 3 };
 bool result = _rule.ContainsKey(rule); //result的结果为false

result的结果居然是false,那就是引用类型的缘故,解决方法:需要重写Equals()方法,以及重写GetHashCode()方法。
参考自文章:C#中,对于Dictionary字典类型,使用引用类型做Key的注意事项

2 完整代码

using System;
using System.Collections.Generic;
using System.Linq;

namespace AnimalSystem
{
    // 使用引用类型作为字典的Key,需要重写Equals方法和GetHashCode()方法
    public class ArrayExtend
    {
        int[] arr;
        string content = "";

        public void Init(int[] a)
        {
            int count = a.Count();
            arr = new int[count];
            for (int i = 0; i < count; i++)
            {
                arr[i] = a[i];
                content += arr[i] + ",";
            }
        }

        public override bool Equals(object obj)
        {
            ArrayExtend temp22 = (ArrayExtend)obj;
            bool flag = true;
            for (int i = 0; i < arr.Count(); i++)
            {
                if (arr[i] != temp22.arr[i])
                    flag = false;
            }
            return flag;
        }

        public override int GetHashCode()
        {
            return content.GetHashCode();
        }
    }
    class Program
    {

        static void Main(string[] args)
        {
            // 准备知识库
            Dictionary<int, string> _knowledgeBase = new Dictionary<int, string>()
            {
                    {1, "有毛发"},{2, "有奶 "},{3 ,"有羽毛"},{4 ,"会飞 "},
                    {5 ,"会下蛋"},{6 ,"眼盯前方"},{7 ,"有蹄 "},{8 ,"反刍动物"},
                    {9 ,"吃肉 "},{10 ,"有犬齿"},{11 ,"有爪"},{12 ,"长脖子"},
                    {13 ,"长腿"},{14 ,"有暗斑点"},{15 ,"有黑色条纹"},{16 ,"黄褐色"},
                    {17 ,"不会飞"},{18 ,"黑白二色"},{19 ,"会游泳"},{20 ,"擅飞"}
            };

            // 这里使用的是int[]数组类型作为字典的Key,其使用方法引用自文章
            // https://www.cnblogs.com/WangSong-Gemini/p/16407833.html
            // 准备规则库(命名规则:第一个数字代表动物编号,第二个数字代表该动物的条件编号)
            Dictionary<ArrayExtend, string> _ruleBase = new Dictionary<ArrayExtend, string>();
            ArrayExtend a11 = new ArrayExtend();
            ArrayExtend a12 = new ArrayExtend();
            ArrayExtend a13 = new ArrayExtend();
            ArrayExtend a14 = new ArrayExtend();
            ArrayExtend a21 = new ArrayExtend();
            ArrayExtend a22 = new ArrayExtend();
            ArrayExtend a23 = new ArrayExtend();
            ArrayExtend a24 = new ArrayExtend();
            ArrayExtend a31 = new ArrayExtend();
            ArrayExtend a32 = new ArrayExtend();
            ArrayExtend a33 = new ArrayExtend();
            ArrayExtend a34 = new ArrayExtend();
            ArrayExtend a41 = new ArrayExtend();
            ArrayExtend a42 = new ArrayExtend();
            ArrayExtend a43 = new ArrayExtend();
            ArrayExtend a44 = new ArrayExtend();
            ArrayExtend a51 = new ArrayExtend();
            ArrayExtend a52 = new ArrayExtend();
            ArrayExtend a61 = new ArrayExtend();
            ArrayExtend a62 = new ArrayExtend();
            ArrayExtend a71 = new ArrayExtend();
            ArrayExtend a72 = new ArrayExtend();
            a11.Init(new int[] { 1, 7, 15 });
            a12.Init(new int[] { 2, 7, 15 });
            a13.Init(new int[] { 1, 8, 15 });
            a14.Init(new int[] { 2, 8, 15 }); // 斑马
            a21.Init(new int[] { 1, 7, 12, 13, 14 });
            a22.Init(new int[] { 2, 7, 12, 13, 14 });
            a23.Init(new int[] { 1, 8, 12, 13, 14 });
            a24.Init(new int[] { 2, 8, 12, 13, 14 }); // 长颈鹿
            a31.Init(new int[] { 1, 9, 14, 16 });
            a32.Init(new int[] { 2, 9, 14, 16 });
            a33.Init(new int[] { 1, 6, 10, 11, 14, 16 });
            a34.Init(new int[] { 2, 6, 10, 11, 14, 16 }); // 金钱豹
            a41.Init(new int[] { 1, 9, 15, 16 });
            a42.Init(new int[] { 2, 9, 15, 16 });
            a43.Init(new int[] { 1, 6, 10, 11, 15, 16 });
            a44.Init(new int[] { 2, 6, 10, 11, 15, 16 }); // 虎
            a51.Init(new int[] { 3, 12, 13, 17, 18 });
            a52.Init(new int[] { 4, 5, 12, 13, 17, 18 }); // 鸵鸟
            a61.Init(new int[] { 3, 20 });
            a62.Init(new int[] { 4, 5, 20 }); // 信天翁
            a71.Init(new int[] { 3, 17, 18, 19 });
            a72.Init(new int[] { 4, 5, 17, 18, 19 }); // 企鹅
            _ruleBase.Add(a11, "斑马");
            _ruleBase.Add(a12, "斑马");
            _ruleBase.Add(a13, "斑马");
            _ruleBase.Add(a14, "斑马");
            _ruleBase.Add(a21, "长颈鹿");
            _ruleBase.Add(a22, "长颈鹿");
            _ruleBase.Add(a23, "长颈鹿");
            _ruleBase.Add(a24, "长颈鹿");
            _ruleBase.Add(a31, "金钱豹");
            _ruleBase.Add(a32, "金钱豹");
            _ruleBase.Add(a33, "金钱豹");
            _ruleBase.Add(a34, "金钱豹");
            _ruleBase.Add(a41, "虎");
            _ruleBase.Add(a42, "虎");
            _ruleBase.Add(a43, "虎");
            _ruleBase.Add(a44, "虎");
            _ruleBase.Add(a51, "鸵鸟");
            _ruleBase.Add(a52, "鸵鸟");
            _ruleBase.Add(a61, "信天翁");
            _ruleBase.Add(a62, "信天翁");
            _ruleBase.Add(a71, "企鹅");
            _ruleBase.Add(a72, "企鹅");


            // 输出知识库
            Console.WriteLine("===============================动物识别系统规则库===============================");
            foreach (var item in _knowledgeBase)
            {
                Console.Write("{0}:{1}\t", item.Key, item.Value);
                if (item.Key % 5 == 0) // 控制换行的逻辑
                {
                    Console.WriteLine();
                }
            }

            // 获取用户输入
            Console.WriteLine("================================================================================");
            Console.WriteLine("请输入已知条件(用空格分开),按回车结束:");
            
            // 获取用户输入
            string userInput = Console.ReadLine();
            // 拆分用户输入
            string[] inputx = userInput.Split(' ');
            // 将用户输入的字符串转换成int数组
            int[] inputy = new int[inputx.Length];
            for (int i = 0; i < inputy.Length; i++)
            {
                inputy[i] = Convert.ToInt32(inputx[i]);
            }
            // 反馈给用户检查
            Console.WriteLine("================================================================================");
            Console.Write("请检查您的输入:");
            foreach(var item in inputy)
            {
                Console.Write(item + " ");
            }
            Console.WriteLine();
            Console.Write("Y/N?: ");
            string inputCheck = Console.ReadLine();
            if(inputCheck.Equals("Y") || inputCheck.Equals("y"))
            {
                Console.WriteLine("OK");
                // 将用户的输入排个序
                SortArray(inputy);
            }
            else if(inputCheck.Equals("N") || inputCheck.Equals("n"))
            {
                // 实现跳转到重新输入的逻辑?(待实现)
                Console.WriteLine("Error");
                
            }
            else
            {
                Console.WriteLine("请重新输入:");
                throw new Exception("输入错误");
                // 跳转到重新输入的逻辑?
            }
            Console.WriteLine("================================================================================");
            // 进行判断的逻辑
            ArrayExtend inputUser = new ArrayExtend();
            inputUser.Init(inputy);
            try
            {
                // 如果当前规则库中没有存在这个规则在这里就会抛出异常
                string result = _ruleBase[inputUser];
                Console.WriteLine("根据您输入的条件,可以推断出这个动物是:" + result);
            }
            catch (Exception)
            {
                Console.WriteLine("根据您的输入条件,无法判断这是一种什么动物。");
            }
            
            Console.ReadLine();
        }

        // 将数组元素从小到大排序(用户的输入可能不是按照从小到大的顺序,根据规则库的存储结构,需要将用户的输入进行从小到大的排序)
        static void SortArray(int[] arr)
        {
            for (int i = 0; i < arr.Length-1; i++)
            {
                for (int j = i + 1; j < arr.Length; j++)
                {
                    if (arr[j] < arr[i])
                    {
                        int temp = arr[j];
                        arr[j] = arr[i];
                        arr[i] = temp;
                    }
                }
            }
        }
    }
}

3 总结

        代码还有很多不完善的地方,比如说实现反复推理,不是每次运行推理结束后又要点一次运行,又比如说在不小心输入错误了,实现重新输入等等。

你可能感兴趣的:(人工智能,c#,人工智能)