签到

05月06日
尚未签到

共有回帖数 0

    长街旧港00

    等级:
    先贴地址:
    技术分析文章:http://hi.baidu.com/naylonslain/blog/item/88375923caadb4e4d6cae246.html
    源代码下载地址:http://naylon.0ginr.com/download/ChemicalEquation.rar
    如果您关注这个小程序的动态,可以浏览:http://naylon.0ginr.com/?p=15
    最近学了点数学知识,况且考虑到算法一直是自己的弱项,就写了这个来练习一下……(同时也是受到了XiaoJ的启发) 源代码下载地址:http://naylon.0ginr.com/download/ChemicalEquation.rar
    如果您关注这个小程序的动态,可以浏览:http://naylon.0ginr.com/?p=15

    Before:
    上百度百科查了一下,化学方程式配平大概有以下几种方法:
    1-最小公倍数法
    2-观察法
    3-奇偶配平法
    4-待定系数法
    5-化合价法

    大概看了看,如果要让计算机求解,4自是最好的选择,可以编写算法,于是选4下手。

    PART0:Example
    先举个配平的例子,就用H2+O2=H2O吧,比较简单:

    先设系数分别为X0, X1, X2,得
    X0 H2 + X1 O2 = X2 H2O

    于是可得方程组
    2X0 = 2X2
    2X1 = X2

    显然,这样的方程组难以求解,于是设系数X0为1,可得
    -2X2 = -2
    2X1 - X2 = 0

    这样就把配平的问题转化成了解n元线性方程组的问题,接下来就是直观的问题了:如何解?考虑到不同的方程式可能有n个未知数,所以要找一种通用的解n元线性方程组的方法。于是我使用了矩阵行列式的方法(关于行列式的具体内容可以参考附带的ppt,内容较多,不再阐述)。

    接上,可得行列式
    D = 0 * (-1) – (-2) * 2 = 4
    D1 = (-2) * (-1) - (-2) * 0 = 2
    D2 = 0 * 0 - (-2) * 2 = 4

    于是得解
    X0 = 1 (我们自己设的)
    X1 = D1 / D = 2 / 4 = 1 / 2
    X2 = D2 / D = 4 / 4 = 1

    化成整数
    X0 = 2
    X1 = 1
    X2 = 2

    正解:2H2+O2=2H2O


    PART1:全排列
    首先要编写算法求n阶行列式的解,如果是1=n=3的话,还可以使用对角线法则,可是n阶的话就要找出行列式的规律,学习了一下附带的ppt,可以看到关键的一页:


    “所有不同的三级排列j1j2j3求和”,说明如果要解n阶行列式就需要用到1-n个数的全排列,然后逐一代入上述公式即可得解。

    本来以为生成1-n的全排列很容易,打算几分钟搞定,可是实际一尝试才发现没那么简单(算法水平太菜了),上网COPY了一段倒是很好使,不过没注释没说明看不懂,还是想自己原创一种方法,所以不决定采用网上的代码。当日未能得解,很郁闷。于是睡觉之前就一直想该如何实现。果然找到了好方法,跟网上流传的不太一样,暂称之为“插入法”。

    插入法的思想是什么呢?

    比如1的全排列就是1。1个数。
    2的全排列有12,21。2个数。
    3的全排列有123,132,213,231,312,321。6个数。
    (n的全排列数列一共有n!个)

    我就想啊,如果由1阶上升为2阶,2有2个位置可以插入,分别为1的左侧、右侧,可以分别得到21,12。

    如果由2阶上升为3阶,那么,针对21,3有3个位置可以插入,可以得到321,231,213;针对12,3也有3个位置可以插入,可以得到312,132,123。.

    如果由n-1阶上升为n阶,那么,对于每个n-1阶的全排列数列,n有n个位置可以插入,可以得到n的所有全排列数列。

    显然,这种方法可以用一个递归来解决。实现代码如下(很多地方肯定还有瑕疵啦,还望各位大牛们赐教):


    // 插入法核心递归函数
    void p_insert_perm(unsigned char m, unsigned char
    *flist)
    {
        unsigned char i = 1;
        unsigned char *list = (unsigned char*)malloc(m + 1);
        memcpy(&list[1], flist, m - 1);
        list[0] = m;
        do {
            if (m  g_n) {
                p_insert_perm(m + 1, list);
            } else {
                add_record(list);
            }
            swap((char*)&list[i - 1], (char*)&list);
        } while (++i = m);
        free(list);
        return;
    }

    // 使用插入法生成全排列表
    void insert_perm(unsigned char n)
    {
        g_n = n;
        g_i = 0;
        unsigned char *list = (unsigned char*)malloc(2);
        list[0] = 1;

        p_insert_perm(2, list);

        free(list);
    }
    测试了一下效果还是不错的,效率跟网上的代码也差不多,算是解决了全排列问题。

    PART2:SolveDeterminant
    这个就简单多了,只要按照全排列数列对系数矩阵进行乘、加运算就行了。
    代码如下:
    首先定义了一个全排列数列数组:

    unsigned char **perm_list = 0;

    long solve_det(char *det, unsigned char n)
    {
        unsigned long i = 0;
        long k = 1, count = 0;
        unsigned char j = 0, m = 0;

        for (; i  g_amount; i++) {
            for (k = 1, j = 0; j  n; j++) {
                m = *(unsigned char*)((unsigned char*)perm_list + j);
                k = k * det[j * n + m - 1];
            }
            if (k != 0) {
                k = is_even_array(perm_list, n)? k: k * (-1);
                count = count + k;
            }
        }

        return count;
    }
    PART3:StringAnalyze
    字符串分析……就是把用户输入的方程式的系数提出来转换成系数矩阵,然后进行求解,这段代码相对较长,纯体力活,实在没啥意思,就不贴了,大家自己下载看吧……

    Problems:
    问题还是相当多的:
    1、 有些特别复杂的方程式会出错(八成是字符串解析的问题,没啥技术,暂时不想耗费时间修改了)
    2、 不支持有机化学(同上)
    3、 某些形式特殊的方程式也会出错(比如KClO3=KCl+O2和CH3COOH+NaCl=HCl+CH3COONa,为啥呢?大家按照我开头给的例子列一下方程组就知道了,有两个式子是相同的,所以无法求得正解)

    最近还想研究点别的东西,这个小玩意就先告一段落了,毕竟目的只是为了学习相关知识。CUI十分恶心,兴许暑假时会再完善一下,解决上述问题。

    最终效果:



    我的文笔真的是相当差……希望各位大牛们不吝赐教~偶只是一只算法菜鸟~数学菜鸟~编程菜鸟……但是我会努力学习的!

    楼主 2015-11-19 21:08 回复

共有回帖数 0
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知