美文网首页
行列式的一种解法

行列式的一种解法

作者: kikyoulzg | 来源:发表于2017-09-17 23:12 被阅读0次

    code:

     #include <malloc.h>
    #include <stdio.h>
    #include <stdlib.h>             
    #include <stdbool.h>     //计算行列式的值,要bool类型的可以直接用stdbool.h~\(≧≦)/~啦啦啦(c99)//
                                      
    int * c,                       
          n = 0,                  
          a,                         
          sum = 0;             
    int aq(int a)           /*aq计算阶乘*/              
    {
        int s = 1;
        for(int i = 1; i <= a; i ++)
              s *= i;
        return s;
    }
    void swap(int * a, int * b)         /*swap利用地址传递交换两个数的值*/
    {
        int m =* a;
        * a = * b;
        *b = m;
    }
    bool sa(int * l)            /*sa计算在行列式计算过程中每一项前边的符号是正还是负*/
    {
        int n = 0;
        for(int i = 0; i < a-1; i ++)
             for(int j = i+1; j < a; j++)
                   if(l[i]>l[j])n++;
        if(n % 2 == 0) return false;
        return true;
    }
    void perm(int * l,int k,int m)          /*perm整个程序里边的核心函数,找出在不同行不同列的所有组合*/
    {
        int i, s = 1;
        if(k > m)
        {
            n++;            //递归次数//
            for(int j = 0; j < a; j ++)
                  s *= c[ l[ j ] + a * j ];
            if( sa( l ) ) s*=-1;
          
            printf("%5d      完成度:%2.2f%%\n", sum+=s , n/( aq( a ) * 0.1 ) * 10 );
        }
        else            //全排列//
        {
            for(i = k; i <= m; i++)
            {
                swap(l + k, l + i);
                perm(l, k + 1, m);
                swap(l + k, l + i);
            }
        }
    }
    /*---------------------------------------------------------------*/
    void main()
    {
        int * b,
        i, 
        f, 
        e;
        system("color 3e");
    u: system("cls");
        printf("请输入行列式的阶数:\n");
        scanf("%d", &a);//获取行列式的阶数
        b = ( int * ) malloc ( sizeof ( int ) * a );            /*malloc*/
        c = ( int * ) malloc ( sizeof ( int ) * a * a );
        for( i = 0; i < a; i++)
             * ( b + i ) = i;
        for( i = 0; i < a * a; i++)
        {
            if( i % a == 0 ){
                printf("请依次输入行列式中第%d行的值(以空格分隔):\n",i / a + 1 );
        }
            scanf("%d", c + i );
        }
        printf("\n\n");
        perm( b, 0, a - 1 );            /*计算行列式的值*/
        printf("\n行列式展开式共有%d项\n", aq( a ) );
        if ( a % 2 != 0 ) f = a + 1;
            else f = a;
        for( i = 0; i < a * a; i ++ )
        {       
            if ( i / a + 1 == f / 2 && i % a == 0) 
                printf("D = ");//输出“D = ”
            else if ( i % a == 0) 
                        printf("    ");
            if ( i % a == 0) 
                printf("┃");
            if ( ( i + 1 ) % a == 0) 
                printf("%2d", * ( c + i ) );
            else printf("%2d ", * ( c + i ) );
            if ( ( i + 1 ) % a == 0 ) 
                printf("┃");
            if ( ( i + 1 ) / a == f / 2 && ( i + 1 ) % a == 0) 
                printf(" = %d\n",sum);
            else if ( ( i + 1 ) % a == 0 ) 
                        printf("\n");
        }
        printf("\n\n");
        printf("是否继续?( 1 / 0 )\n");
        scanf("%d", &e);
        n = 0;
        if( e ==1 ) goto u;
        else if ( e == 0 ) exit( 0 );
       }
    

    dosomethink

    • 这个程序利用了行列式的定义来计算行列式的值。也就是找出行列式中所有不同行不同列的所有组合,分别计算每个组合里的数的乘积再乘以-1的它们的逆序数次方,最后累加。(表达能力不好,看不懂可以谷歌一下)


    • 看代码本身,主函数做的事无非读取行列式然后计算。这是读取行列式的代码。
        printf("请输入行列式的阶数:\n");
        scanf("%d", &a);//获取行列式的阶数
        b =  malloc ( sizeof ( int ) * a );            /*malloc*/
        c =  malloc ( sizeof ( int ) * a * a );
        for( i = 0; i < a; i++)
             * ( b + i ) = i;
              printf("hallo");
        for( i = 0; i < a * a; i++)
        {
            if( i % a == 0 ){
                printf("请依次输入行列式中第%d行的值(以空格分隔):\n",i / a + 1 );
        }
            scanf("%d", c + i );
        }
    

    我修改了一下

        scanf("%d", &a);//获取行列式的阶数
        b = ( int * ) malloc ( sizeof ( int ) * a );            /*malloc*/
        c = ( int * ) malloc ( sizeof ( int ) * a * a );
        for( i = 0; i < a; i++)
             * ( b + i ) = i;       //不会到这行了?//
         printf("hallo\n") ;     
        for( i = 0; i < a * a; i++)
        {
            if( i % a == 0 ){
                printf("请依次输入行列式中第%d行的值(以空格分隔):\n",i / a + 1 );
            }
                
            scanf("%d", c+i );
            printf("%d | ,%d | ,%d\n",i,c[i],b[i] ); 
            
        }
    

    下面是阶数为2的运行结果

    请输入行列式的阶数:
    2
    hallo
    请依次输入行列式中第1行的值(以空格分隔):
    1 2
    0 | ,1 | ,0
    1 | ,2 | ,1
    请依次输入行列式中第2行的值(以空格分隔):
    3 4
    2 | ,3 | ,0
    3 | ,4 | ,0
    
    
        4      完成度:50.00%
       -2      完成度:100.00%
    
    行列式展开式共有2项
    D = ┃ 1  2┃ = -2
        ┃ 3  4┃
    

    我不懂的是hallo就出现了一次,说明循环只经过第一个for的函数体一次,但是从1 | ,2 | ,1这行输出看却经过了。不懂。

    • 然后是计算,下面是用来计算的perm函数
    void perm(int * l,int k,int m)          /*perm整个程序里边的核心函数,找出在不同行不同列的所有组合*/
    {
        int i, s = 1;
        if(k > m)
        {
            n++; 
            for(int j = 0; j < a; j ++)
                  s *= c[ l[ j ] + a * j ];
            if( sa( l ) ) s*=-1;
          
            printf("%5d      完成度:%2.2f%%\n", sum+=s , n/( aq( a ) * 0.1 ) * 10 );
        }
        else  
        {
            for(i = k; i <= m; i++)
            {
                swap(l + k, l + i);
                perm(l, k + 1, m);
                swap(l + k, l + i);
            }
        }
    }
    

    这里涉及了全排列算法,这里是用了递归的全排列算法。我参考了一篇博客。

    全排列是将一组数按一定顺序进行排列,如果这组数有n个,那么全排列数为n!个。现以{1, 2, 3, 4, 5}为
    
    例说明如何编写全排列的递归算法。
    
    1、首先看最后两个数4, 5。 它们的全排列为4 5和5 4, 即以4开头的5的全排列和以5开头的4的全排列。
    
    由于一个数的全排列就是其本身,从而得到以上结果。
    
    2、再看后三个数3, 4, 5。它们的全排列为3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六组数。
    
    即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合.
    
    从而可以推断,设一组数p = {r1, r2, r3, ... ,rn}, 全排列为perm(p),pn = p - {rn}。
    
    因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), ... , rnperm(pn)。当n = 1时perm(p} = r1。
    
    为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。
    

    前面都懂,就是最后一句没懂

    为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。

    博客里的算法示例

    #include <stdio.h>  
    
    int n = 0;  
    
    void swap(int *a, int *b) 
    {     
        int m;     
        m = *a;     
        *a = *b;     
        *b = m; 
    }  
    void perm(int list[], int k, int m) 
    {     
        int i;     
        if(k > m)     
        {          
            for(i = 0; i <= m; i++)             
                printf("%d ", list[i]);         
            printf("\n");         
            n++;     
        }     
        else     
        {         
            for(i = k; i <= m; i++)         
            {             
                swap(&list[k], &list[i]);             
                perm(list, k + 1, m);             
                swap(&list[k], &list[i]);         
            }     
        } 
    } 
    int main() 
    {     
        int list[] = {1, 2, 3, 4, 5};     
        perm(list, 0, 4);     
        printf("total:%d\n", n);     
        return 0; 
    }  
    

    运行结果显示n=120,但n=1以后我就不知道怎么回事了。
    swap(&list[k], &list[i]); perm(list, k + 1, m); swap(&list[k], &list[i]);
    第5次递归后,k>m了,执行
    for(i = 0; i <= m; i++) printf("%d ", list[i]); printf("\n"); n++;
    输出12345,然后n++。这之后应该执行perm函数后面的那个swap函数了吧,这时&list[k], &list[i]里的i是多少呢?接下来又怎么回事呢。

    来补个坑

    上面的问题总算搞懂了。

    打印hallo的语句不在for语句里面。。。

    全排列以{A,B,C}为例:
    第一次,A和A互换位置 (A和A互换位置,位置不变),还是A,{B,C}; {B,C}继续做全排列(递归);恢复A和A的位置。
    第二次,A和B互换位置(第二个数与第一个数交换),变成B,{A,C}; {A,C}继续做全排列(递归);恢复A和B的位置。
    第三次,A和C互换位置(第三个数与第一个数交换),变成C,{B,A}; {B,A}继续做全排列(递归);恢复A和C的位置。

    这就是“将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。”

    以上是deepin论坛里的网友给的解答,非常感谢他。原理正如他说的那样,但是我跟着代码演算的过程是这样的(相关链接里的第一个链接的程序,{1,2,3})


    DeepinScreenshot.png

    从原理到代码级别的实现还是有点难度啊,不过食用代码的过程我也吸收了不少营养(递归、全排列、用malloc动态分配内存)。

    来点感悟

    写程序是用代码实现人的想法,所以怎么把想法转变成代码呢,暂时觉得是这样的:设计main的草图)造main需要的零件)造零件需要的零件)....)组装零件)打磨打磨?加点润滑油?涂点漆?扔进垃圾桶?

    相关链接

    全排列算法原理和实现
    n阶行列式计算

    相关文章

      网友评论

          本文标题:行列式的一种解法

          本文链接:https://www.haomeiwen.com/subject/gqzpsxtx.html