Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

行列式的计算与矩阵的逆

  在四则运算中,一般只需要定义加法和乘法。除法和减法只是他们的逆运算,这里就需要逆的概念。我们对矩阵乘法的定义是变换,类似复数和四元素的乘法。因此矩阵的除法就是逆变换,对一个矩阵求逆矩阵就相当于使用一个单位矩阵去除以那个矩阵。
  逆矩阵与原矩阵的关系是这样的,这里拿三阶矩阵举例。
  
  其实这个例子还不太直观,应该看个四阶的例子。不过四阶的图画出来太可怕了,做这种图很麻烦的我就偷个懒了。等号右边分数线上方有个巨大的矩阵(Adjugate Matrix),这个矩阵中每一项都是一个行列式。由于原矩阵是三阶的,这里的行列式就全是3-1=2阶的。也就是说,这个大矩阵中的行列式比原矩阵低一阶。
  这个大矩阵里面的东西乍看之下很乱吧?其实把原矩阵中所有相邻(头尾可相接)的四个都看做一个2阶的矩阵就可以得到这个大矩阵。只是他们分布的位置与原来稍微不同,原来是横着排列的,变成了竖着排列,也就是行列倒置了。不过整个变换的方向并没有倒置,原矩阵是:红→绿→蓝,右边的大矩阵是:绿→蓝→红,蓝→红→绿。这意味着如果原来是右手法则现在依然保持右手法则。最后还有注意原矩阵左上角的二阶行列式{{a,b},{i,j}}在大矩阵的右下角。这是我自己的理解,与线性代数的中的介绍会有点不同,不过原理是一样的。
  这里没画四阶的矩阵,但实际上对于偶数阶的矩阵,在生成的大矩阵中的每个行列式前都有个符号处理。行号和列号(从0开始计算)的和为奇数时符号是负的。这可以从二阶矩阵上看出

  所以计算偶数阶矩阵的逆时需要额外做个符号处理。其实是因为我的算法有点奇怪所以才有这个问题,如果完全按照标准定义来的话无论是阶数的奇偶都需要做符号处理。
  了解了这些规律就可以开始写程序了,首先遍历原矩阵,并准备一个数组存放右边大矩阵的计算结果。原矩阵从左上角开始遍历,那么写入大矩阵时应该从右下角开始,这只要从循环变量上加一个常量就可以得到。然后我们还知道它是行列倒置的,所以如果循环变量是i和j(这只是循环变量,和前面图中的ij无关),那么写入大矩阵时就需要用j和i。最后,我们还知道两个矩阵的方向相同,也就是说,如果遍历时是i++和j++,那么大矩阵中也是加的,它不会变成减法。不过要注意,由于大矩阵是从右下角开始的,递加之后循环变量一定会超出了矩阵本身,所以使用时应该把它和矩阵的阶数做取余运算,这样超出后可以回到左上角重新开始。
function inverse(e){
  var i,j,m,n,r,o,s,l,b;
  //计算原矩阵的行列式值
  s=determinant(e);
  //如果行列式的值为0则抛出异常
  if(s==0)return console.error("该矩阵不可逆");
  //计算矩阵阶数阶数
  l=Math.sqrt(e.length),b=l%2==0;
  //遍历原矩阵
  for(r=[],i=0;i<l;i++)for(j=0;j<l;j++){
    //遍历生成大矩阵中的行列式
    for(o=[],m=0;m<l-1;m++)for(n=0;n<l-1;n++)
      //处理矩阵的部分,并从原矩阵取出值放入行列式中
      o.push(e[((i+m+1)%l)*l+(j+n+1)%l]);
    //计算行列式的值,并放入大矩阵
    r[i+j*l]=(b&&(i+j)%2?-1:1)*determinant(o);
  };
  //把原矩阵的行列式值逐项除进大矩阵
  for(i=0;i<r.length;i++)r[i]/=s;
  //返回结果
  return r;
};
  这里还用到了determinant函数,也就是计算行列式的值。这个原理也没啥好说的,这里用了降级递归的方式来实现的。 function determinant(e){
  var i,j,s,o,l;
  //计算并判断矩阵的阶数
  switch(l=Math.sqrt(e.length)){
    case 1:return e[0];
    //二阶和三阶直接主对角线减副对角线
    case 2:return e[0]*e[3]-e[1]*e[2];
    case 3:return (
      e[0]*e[4]*e[8]-e[0]*e[5]*e[7]+
      e[1]*e[5]*e[6]-e[1]*e[3]*e[8]+
      e[2]*e[3]*e[7]-e[2]*e[4]*e[6]
    );
    //高阶则需要降阶
    default:
      //取行列式第一行展开
      for(i=s=0;i<l;i++){
        //生成展开位置的低阶行列式
        for(j=l,o=[];j<e.length;j++)if(j%l!=i)o.push(e[j]);
        //计算展开后每个低阶行列式的值,并计算符号来连接
        s+=determinant(o)*e[i]*(i%2?-1:1);
      };
      //返回结果
      return s;
  };
};
网名:
3.80.32.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^