C#化学物质/方程式流程图题自动推导程序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#化学物质/方程式流程图题自动推导程序相关的知识,希望对你有一定的参考价值。

   这学期才学了化学,四个星期前才做了一种化学流程推导题,你需要根据框框里给的已知物质,一堆箭头的指向,箭头上方的已知的条件,推导出要求求出的对应的框框的物质,如图:

技术分享
  

  有时候只是箭头,一个已知的物质都没有,所以要将所有的物质推导出来是十分的困难,于是在我做这种题时我想到了一个好点子:能不能自己写一个程序,填入已知的数据包括反应物,然后自己生成所有的有可能的正确的解决方案?

  有了这个想法,我便从晚自习(学校住校不允许携带电子设备)开始思考,用一个正确的结构来输入和输出数据,不仅方便写入和读取,还能方便展示,在两天的冥思苦想之后,终于想出了一个好点的结构,并想到了树的算法(并不是规范的树,仅仅引用了思想)

 技术分享

 

并没有像Server的代码那样层次构架那么复杂,函数嵌套调用,此程序只是用了一层,其余全是一环套一环的对象封包。  (其中Test为测试项目,ChemicalEquation为界面,可忽略)

 

下面详细讲讲每个项目的作用:

 

Model:

 

  AllData_IN

  

  此类为输入,输出数据的封装,方便数据的处理。

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

namespace Model
{
    public class AllData_IN
    {
        private int nodeThis;

        public int NodeThis
        {
            get { return nodeThis; }
            set { nodeThis = value; }
        }

        private ArrayList nodeNext = new ArrayList();

        public ArrayList NodeNext
        {
            get { return nodeNext; }
            set { nodeNext = value; }
        }

        private DataPack_Input data = new DataPack_Input();

        public DataPack_Input Data
        {
            get { return data; }
            set { data = value; }
        }

        private bool isLoaded = false;

        public bool IsLoaded
        {
            get { return isLoaded; }
            set { isLoaded = value; }
        }
    }
}

 

NodeThis是当前节点的标记,NodeNext是下一个节点对象的存储,由于推断题中,孩子节点不止一个,所以用了ArrayList来存储,Data是当前存储的节点数据,IsLoaded是在箭头有回指时判定此节点是否已读取的属性,防止无限死循环。当前数据和下一个节点全部使用DataPack_Input对象来封装数据。

下面即将介绍的DataPack项目,包含了此输入类型。

 

  AllData_OUT

  此类为输出类,包括了输出数据的封装,方便输出数据解析。

 

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

namespace Model
{
    public class SolutionSet_Output
    {
        private ArrayList solutionSets = new ArrayList();

        public ArrayList SolutionSets
        {
            get { return solutionSets; }
            set { solutionSets = value; }
        }
    }

    public class Solution_Output
    {
        private ArrayList solutions = new ArrayList();

        public ArrayList Solutions
        {
            get { return solutions; }
            set { solutions = value; }
        }
    }

    public class Equation
    {
        private ArrayList reactants = new ArrayList();

        public ArrayList Reactants
        {
            get { return reactants; }
            set { reactants = value; }
        }

        private ArrayList products = new ArrayList();

        public ArrayList Prodcts
        {
            get { return products; }
            set { products = value; }
        }

        private ArrayList conditions = new ArrayList();

        public ArrayList Conditions
        {
            get { return conditions; }
            set { conditions = value; }
        }

        public int nodeNUM;

        public int NodeNUM
        {
            get { return nodeNUM; }
            set { nodeNUM = value; }
        }
    }
}

  SolutionSet_OutPut是输出的所有解决方案的object数组,存放用户输入的数据处理后所有有可能的情况,相当于存放了所有节点发生的化学反应所对应的方程式。而Solution_OutPut就是用来存放一个个化学方程式的封装类。而每一个方程式,都是由Equation类封装,每个Equation类的封装,都由四部分组成:反应物数组(Reactents)、条件数组(Conditions)、生成物数组(Products)和当前方程式对应的标记(NodeNum),标记则是为了方便在用户输入的一对节点当中去区分对应输出的方程式到底是属于哪个节点,与AllData_IN当中的NodeThis对应。

 

 

  DataPack

  DataPack_Input是我自己建立的一个封装数据的类。代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Data;

namespace Model
{
    public class ItemPack_Material
    {
        private Materials item = new Materials();

        public Materials Item
        {
            get { return item; }
            set { item = value; }
        }

        private bool isKnown = true;

        public bool IsKnown
        {
            get { return isKnown; }
            set { isKnown = value; }
        }
    }

    public class ItemPack_Condition
    {
        private Conditions item = new Conditions();

        public Conditions Item
        {
            get { return item; }
            set { item = value; }
        }

        private bool isKnown = true;

        public bool IsKnown
        {
            get { return isKnown; }
            set { isKnown = value; }
        }
    }

    public class DataPack_Input
    {
        private ArrayList reactents = new ArrayList();

        public ArrayList Reactent
        {
            get { return reactents; }
            set { reactents = value; }
        }

        private ArrayList conditions = new ArrayList();

        public ArrayList Conditions
        {
            get { return conditions; }
            set { conditions = value; }
        }
    }
}

 ItemPack_Material是用来存放每一个物质的封包(反应物和生成物),里面存的是我的物质的枚举类(Materials),ItemPack_Condition是用来存放每一个条件的封包,里面存的是我的条件的枚举类(Conditions),DataPackInput则是我用来存放输入数据的封包,就是上面AllData_IN这个类的Data(数组)属性存放的封包,里面包含生成物(Reactent)和条件(Conditions)属性,都是数组,分别用来存放ItemPack_Material和ItemPack_Condition的封包数据。

 

Data:

  Materials

  Materials是一个我用来存放物质的枚举类

 

namespace Data
{
    public enum Materials
    {
     O2,
     CO2,
      ......... } }

 

  Conditions

  Conditions是一个我用来存放物质的枚举类

 

namespace Data
{
    public enum Conditions
    {
        Heating,
        HighTempature,
        MnO2,
        FireLighting,
        Light,
        Chloroplast
    }
}

 


Equations:

  DefaultEquations

  这是一个我用来载入和存储所有化学反应方程式的类,包含了一个数组,一个函数。

 

namespace Equations_DATA
{
    public class DefaultEquations
    {
        static private ArrayList allEquations = new ArrayList();

        static public ArrayList AllEquations
        {
            get { return allEquations; }
            set { allEquations = value; }
        }

        static public void AddIntoRquationsList()
        {
            Equation equ = new Equation();
            equ.Reactants.Add(Materials.KMnO4);
            equ.Conditions.Add(Conditions.Heating);
            equ.Prodcts.Add(Materials.K2MnO4);
            equ.Prodcts.Add(Materials.MnO2);
            equ.Prodcts.Add(Materials.O2);
            AllEquations.Add(equ);

            ...............................
        }
    }
}

 

AllEquations是用来存放和读取所有化学方程式的数组,而AddIntoEquationsList函数则是用来载入所有方程式的,是在可启动项目启动时调用的。

 

以上是我的数据模式和结构,接下来将展示我的算法

  算法是一个函数,包括在EQUA_Dal项目里面。我的方法大概是这样:输入树的头节点,然后在读取输入节点中的Data中的数据,不断的拆箱,与我载入的AllEquations数组中的数据作比较,匹配得出方程式,先匹配反应物,再匹配条件,每一次匹配都有精密的算法,再将最后得出的对于这一个节点数据匹配出的几个最佳方程式保存在一个ArrayList数组中,然后再开始匹配下一个节点,使用每一个匹配到的方程式的生成物,匹配下一个节点的反应物,若有一个或多个物质是符合的,那么将形成通路,进行下一个节点的匹配,使用递归算法,在一种解决方案匹配执行的时候不断将成功匹配的方程式存入方程式数组(Solution_OutPut)中,并且每个方程式都由标记标识属于哪个节点。最后当遍历完一种方法或这种方法行不通的时候,将会将这种解决方案匹配成功方程式存放的数组存入一个解决方案数组(SolutionSets_OutPut)中,在所有匹配完成后将解决方案数组返回。

 

以下是代码:

 

EQUA_Dal:

  DataMatch

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Data;
using System.Collections;
using Model;
using Equations_DATA;

namespace DataDal
{
    public class DataMatch
    {
        static SolutionSet_Output output = new SolutionSet_Output();
        static Solution_Output dataLast = new Solution_Output();

        static public SolutionSet_Output Match(AllData_IN data, ArrayList alLast)
        {
            //ArrayList alTemp = new ArrayList();

            //foreach (MyEnumType type in Enum.GetValues(typeof(MyEnumType)))
            //{
            //    // TODO: 遍历操作
            //}
            //foreach (string name in Enum.GetNames(typeof(MyEnumType)))
            //{
            //    // TODO:遍历操作
            //}

            //匹配一个化学式
            ArrayList alResult = new ArrayList();
            data.IsLoaded = true;   //标记已读

            if (data.Data.Reactent.Count == 0)
                alResult = Equations_DATA.DefaultEquations.AllEquations;
            //反应物匹配(遍历用户输入的化学方程式里面所有反应物)
            for (int iData = 0; iData < data.Data.Reactent.Count; iData++)
            {
                //遍历反应物数组
                ArrayList alReactentTemp = (ArrayList)data.Data.Reactent;
                for (int iAlReactentTemp = 0; iAlReactentTemp < alReactentTemp.Count; iAlReactentTemp++)
                {
                    //获取单个元素包
                    ItemPack_Material mReactentTemp = (ItemPack_Material)alReactentTemp[iAlReactentTemp];
                    if (mReactentTemp.IsKnown)
                    {        //若已知此元素,遍历匹配
                        if (alResult.Count == 0)     //若为空,则大面积匹配
                        {
                            for (int iData_AllEquations = 0; iData_AllEquations < DefaultEquations.AllEquations.Count; iData_AllEquations++)
                            {
                                //单个化学方程式反应物元素拆箱                               
                                Equation eqTemp = (Equation)DefaultEquations.AllEquations[iData_AllEquations];
                                for (int iEqTemp = 0; iEqTemp < eqTemp.Reactants.Count; iEqTemp++)
                                {
                                    //遍历并进行第一次匹配
                                    if ((Materials)eqTemp.Reactants[iEqTemp] == mReactentTemp.Item)
                                    {
                                        alResult.Add(eqTemp);
                                        break;

                                    }
                                }
                            }
                        }
                        else   //若不为空,则遍历已第一次匹配
                        {
                            ArrayList alResultTempOverWriteTemp = new ArrayList();
                            for (int iAlResultTempOverWrite = 0; iAlResultTempOverWrite < alResult.Count; iAlResultTempOverWrite++)
                            {
                                //单个化学方程式反应物元素拆箱                               
                                Equation eqTemp = (Equation)alResult[iAlResultTempOverWrite];
                                for (int iEqTemp = 0; iEqTemp < eqTemp.Reactants.Count; iEqTemp++)
                                {
                                    //遍历是否与之前匹配出来的其他元素匹配
                                    if ((Materials)eqTemp.Reactants[iEqTemp] == mReactentTemp.Item)
                                        alResultTempOverWriteTemp.Add(eqTemp);
                                }
                            }
                            alResult = alResultTempOverWriteTemp;
                        }
                    }
                    else                                   //若未知
                    {

                    }
                }

            }

            //条件匹配
            for (int iData = 0; iData < data.Data.Conditions.Count; iData++)
            {
                ArrayList alConditionsTemp = (ArrayList)data.Data.Conditions;
                //遍历所有条件获取单个进行匹配
                for (int iAlConditionsTemp = 0; iAlConditionsTemp < alConditionsTemp.Count; iAlConditionsTemp++)
                {
                    ItemPack_Condition cConditions = (ItemPack_Condition)alConditionsTemp[iAlConditionsTemp];
                    if (cConditions.IsKnown)  //是否为已知条件
                    {
                        //为已知条件
                        if (alResult.Count == 0)
                        {//若为空项
                            for (int iConditions_Equations = 0; iConditions_Equations < DefaultEquations.AllEquations.Count; iConditions_Equations++)
                            {
                                Equation equ = (Equation)DefaultEquations.AllEquations[iConditions_Equations];
                                for (int iEquConditions = 0; iEquConditions < equ.Conditions.Count; iEquConditions++)
                                {
                                    if ((Conditions)equ.Conditions[iEquConditions] == cConditions.Item)
                                    {
                                        alResult.Add(equ);
                                        break;
                                    }
                                }
                            }
                        }
                        else
                        {
                            //不为空项
                            ArrayList alResultTempOverWriteTemp = new ArrayList();
                            for (int iAlResultTempOverWrite = 0; iAlResultTempOverWrite < alResult.Count; iAlResultTempOverWrite++)
                            {
                                //单个化学方程式反应物元素拆箱                               
                                Equation eqTemp = (Equation)alResult[iAlResultTempOverWrite];
                                for (int iEqTemp = 0; iEqTemp < eqTemp.Conditions.Count; iEqTemp++)
                                {
                                    //遍历是否与之前匹配出来的其他元素匹配
                                    if ((Conditions)eqTemp.Conditions[iEqTemp] == cConditions.Item)
                                        alResultTempOverWriteTemp.Add(eqTemp);
                                }
                            }
                            alResult = alResultTempOverWriteTemp;
                        }
                    }
                    else
                    {
                        //不为已知条件
                    }
                }
            }

            //所有匹配结束
            //开始检验下一个节点的正确性

            //开始递归调用
            ArrayList alNext = data.NodeNext;

            for (int iAlNext = 0; iAlNext < alNext.Count; iAlNext++)
            {
                AllData_IN dataNext = (AllData_IN)alNext[iAlNext];   //取出用户定义下一节点的数据
                if (data == null)
                    return output;

                for (int iDataNext = 0; iDataNext < dataNext.Data.Reactent.Count; iDataNext++)
                {
                    ItemPack_Material itemNext = (ItemPack_Material)dataNext.Data.Reactent[iDataNext];
                    Materials mat = itemNext.Item;
                    //与下一个节点匹配
                    for (int iAlResult = 0; iAlResult < alResult.Count; iAlResult++)
                    {
                        AllData_IN dataMatchedEquations = new AllData_IN();   //下一个节点准确数据
                        bool isMatched = false;

                        Equation equ = (Equation)alResult[iAlResult];
                        for (int iEqu_Products = 0; iEqu_Products < equ.Prodcts.Count; iEqu_Products++)
                        {
                            if ((Materials)equ.Prodcts[iEqu_Products] == mat)
                            {
                                //是否存在相同元素
                                dataMatchedEquations.Data.Reactent = dataNext.Data.Reactent;
                                dataMatchedEquations.Data.Conditions = dataNext.Data.Conditions;
                                isMatched = true;
                            }
                        }
                        if (!isMatched)
                            continue;

                        //存储此节点数据,并判定是否已读(避免回溯)
                        equ.nodeNUM = data.NodeThis;
                        dataLast.Solutions.Add(equ);
                        if (dataNext.IsLoaded == true)
                            return output;

                        dataMatchedEquations.NodeNext = dataNext.NodeNext;
                        dataMatchedEquations.NodeThis = dataNext.NodeThis;
                    //记录标记 Match(dataMatchedEquations, dataLast.Solutions); } } }
Solution_Output data_OUTPUT = CloneSol_OUTPUT(dataLast); output.SolutionSets.Add(data_OUTPUT); //last = new Solution_Output(); //完成所有逻辑处理 return output; } static private Solution_Output CloneSol_OUTPUT(Solution_Output data) { Solution_Output dataNew = new Solution_Output(); for (int iClone = 0; iClone < data.Solutions.Count; iClone++) dataNew.Solutions.Add(data.Solutions[iClone]); return dataNew; } } }

 

最后谢谢大家坚持认真阅读!界面还在开发,所有代码,结构思想全部是自己出谋划策并编写,如果有什么bug或者逻辑错误,欢迎指出!

 

以上是关于C#化学物质/方程式流程图题自动推导程序的主要内容,如果未能解决你的问题,请参考以下文章

化学反应速率与化学平衡

Python趣用—配平化学方程式

CSP核心代码片段记录

高中选修化学题

2019年12月CSP考试第三题化学方程式解法

波义耳研究空气成分的化学方程式是啥?