code:
#include
#include
#include
#include //计算行列式的值,要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
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})
从原理到代码级别的实现还是有点难度啊,不过食用代码的过程我也吸收了不少营养(递归、全排列、用malloc动态分配内存)。
来点感悟
写程序是用代码实现人的想法,所以怎么把想法转变成代码呢,暂时觉得是这样的:设计main的草图)造main需要的零件)造零件需要的零件)....)组装零件)打磨打磨?加点润滑油?涂点漆?扔进垃圾桶?
相关链接
全排列算法原理和实现
n阶行列式计算