简单的3x3矩阵逆码(C++)

bat*_*tty 36 c++ math matrix matrix-inverse

计算3x3矩阵逆的最简单方法是什么?

我只是在寻找一个简短的代码片段,它可以解决非奇异矩阵,可能使用Cramer的规则.它不需要高度优化.我更喜欢简单而不是速度.我宁愿不链接其他库.

Cor*_*lks 42

这是batty答案的一个版本,但这会计算正确的逆.batty的版本计算逆的转置.

// computes the inverse of a matrix m
double det = m(0, 0) * (m(1, 1) * m(2, 2) - m(2, 1) * m(1, 2)) -
             m(0, 1) * (m(1, 0) * m(2, 2) - m(1, 2) * m(2, 0)) +
             m(0, 2) * (m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0));

double invdet = 1 / det;

Matrix33d minv; // inverse of matrix m
minv(0, 0) = (m(1, 1) * m(2, 2) - m(2, 1) * m(1, 2)) * invdet;
minv(0, 1) = (m(0, 2) * m(2, 1) - m(0, 1) * m(2, 2)) * invdet;
minv(0, 2) = (m(0, 1) * m(1, 2) - m(0, 2) * m(1, 1)) * invdet;
minv(1, 0) = (m(1, 2) * m(2, 0) - m(1, 0) * m(2, 2)) * invdet;
minv(1, 1) = (m(0, 0) * m(2, 2) - m(0, 2) * m(2, 0)) * invdet;
minv(1, 2) = (m(1, 0) * m(0, 2) - m(0, 0) * m(1, 2)) * invdet;
minv(2, 0) = (m(1, 0) * m(2, 1) - m(2, 0) * m(1, 1)) * invdet;
minv(2, 1) = (m(2, 0) * m(0, 1) - m(0, 0) * m(2, 1)) * invdet;
minv(2, 2) = (m(0, 0) * m(1, 1) - m(1, 0) * m(0, 1)) * invdet;
Run Code Online (Sandbox Code Playgroud)

  • 编译器应该能够优化它.别担心. (5认同)
  • 这是最好的答案imo,但它会计算2次,比如m(1,1)*m(2,2) - m(2,1)*m(1,2).请将3个行列式计算的第二部分存储在3个临时双精度中.它们将再次用于计算的最后部分.如果你翻转m(1,0)*m(2,2) - m(1,2)*m(2,0),你甚至可以在没有减号的情况下重复使用它. (2认同)
  • 我写这篇文章主要是为了清晰,不一定是速度.但是你绝对正确地重复了一些操作,可以通过暂时缓存结果并重复使用来避免. (2认同)

Suv*_*apa 32

你为什么不尝试自己编码呢?把它作为挑战.:)

对于3×3矩阵

alt text http://mathworld.wolfram.com/images/equations/MatrixInverse/NumberedEquation3.gif

矩阵逆是

alt text http://mathworld.wolfram.com/images/equations/MatrixInverse/NumberedEquation4.gif

我假设你知道矩阵| A |的决定因素是什么 是.

图像(c)Wolfram | Alphamathworld.wolfram(06-11-09,22.06)

  • 作为LaTeX的粉丝,没有.它们是由Wolfram | Alpha生成的JPEG.:) (6认同)
  • 我要成为那个人。被否决是因为这实际上并没有回答问题——C++ 代码不在这里。 (4认同)
  • 这就是答案 (2认同)
  • 排版也不错.胶乳? (2认同)
  • 很高兴,如果你有时间接受挑战,但坦率地说,它没有回答这个问题:"我只是在寻找一个简短的代码片段,它将为非奇异矩阵提供技巧" (2认同)

bat*_*tty 27

这段代码计算矩阵A 的转置逆:

double determinant =    +A(0,0)*(A(1,1)*A(2,2)-A(2,1)*A(1,2))
                        -A(0,1)*(A(1,0)*A(2,2)-A(1,2)*A(2,0))
                        +A(0,2)*(A(1,0)*A(2,1)-A(1,1)*A(2,0));
double invdet = 1/determinant;
result(0,0) =  (A(1,1)*A(2,2)-A(2,1)*A(1,2))*invdet;
result(1,0) = -(A(0,1)*A(2,2)-A(0,2)*A(2,1))*invdet;
result(2,0) =  (A(0,1)*A(1,2)-A(0,2)*A(1,1))*invdet;
result(0,1) = -(A(1,0)*A(2,2)-A(1,2)*A(2,0))*invdet;
result(1,1) =  (A(0,0)*A(2,2)-A(0,2)*A(2,0))*invdet;
result(2,1) = -(A(0,0)*A(1,2)-A(1,0)*A(0,2))*invdet;
result(0,2) =  (A(1,0)*A(2,1)-A(2,0)*A(1,1))*invdet;
result(1,2) = -(A(0,0)*A(2,1)-A(2,0)*A(0,1))*invdet;
result(2,2) =  (A(0,0)*A(1,1)-A(1,0)*A(0,1))*invdet;

虽然问题规定了非奇异矩阵,但您可能仍想检查行列式是否等于零(或非常接近零)并以某种方式标记它以确保安全.

  • 这段代码实际上为您提供了逆矩阵的TRANSPOSE.在另一个答案中查看公式 (3认同)
  • 简洁,优雅,快速(即使是转置) - 干得好!使用循环和多个函数调用的更通用的解决方案对于这种基本操作来说是过度的和不必要的开销. (3认同)
  • 不要忘记检查行列式是否为零:) (2认同)

Mr.*_*Ree 9

对我们未知(雅虎)海报的所有应有的尊重,我看着这样的代码,只是在里面死了一点.调试字母汤非常难以调试.任何地方的任何一个错字都可能真的毁了你的一整天.可悲的是,这个特殊的例子缺少带有下划线的变量.当我们有a_b-c_d*e_f-g_h时,它会更有趣.特别是当使用_和 - 具有相同像素长度的字体时.

根据他的建议接受Suvesh Pratapa,我注意到:

Given 3x3 matrix:
       y0x0  y0x1  y0x2
       y1x0  y1x1  y1x2
       y2x0  y2x1  y2x2
Declared as double matrix [/*Y=*/3] [/*X=*/3];
Run Code Online (Sandbox Code Playgroud)

(A)当拿一个3x3阵列的小调时,我们有4个感兴趣的值.较低的X/Y索引始终为0或1.较高的X/Y索引始终为1或2.始终!因此:

double determinantOfMinor( int          theRowHeightY,
                           int          theColumnWidthX,
                           const double theMatrix [/*Y=*/3] [/*X=*/3] )
{
  int x1 = theColumnWidthX == 0 ? 1 : 0;  /* always either 0 or 1 */
  int x2 = theColumnWidthX == 2 ? 1 : 2;  /* always either 1 or 2 */
  int y1 = theRowHeightY   == 0 ? 1 : 0;  /* always either 0 or 1 */
  int y2 = theRowHeightY   == 2 ? 1 : 2;  /* always either 1 or 2 */

  return ( theMatrix [y1] [x1]  *  theMatrix [y2] [x2] )
      -  ( theMatrix [y1] [x2]  *  theMatrix [y2] [x1] );
}
Run Code Online (Sandbox Code Playgroud)

(B)现在是行列式:(注意减号!)

double determinant( const double theMatrix [/*Y=*/3] [/*X=*/3] )
{
  return ( theMatrix [0] [0]  *  determinantOfMinor( 0, 0, theMatrix ) )
      -  ( theMatrix [0] [1]  *  determinantOfMinor( 0, 1, theMatrix ) )
      +  ( theMatrix [0] [2]  *  determinantOfMinor( 0, 2, theMatrix ) );
}
Run Code Online (Sandbox Code Playgroud)

(C)逆现在是:

bool inverse( const double theMatrix [/*Y=*/3] [/*X=*/3],
                    double theOutput [/*Y=*/3] [/*X=*/3] )
{
  double det = determinant( theMatrix );

    /* Arbitrary for now.  This should be something nicer... */
  if ( ABS(det) < 1e-2 )
  {
    memset( theOutput, 0, sizeof theOutput );
    return false;
  }

  double oneOverDeterminant = 1.0 / det;

  for (   int y = 0;  y < 3;  y ++ )
    for ( int x = 0;  x < 3;  x ++   )
    {
        /* Rule is inverse = 1/det * minor of the TRANSPOSE matrix.  *
         * Note (y,x) becomes (x,y) INTENTIONALLY here!              */
      theOutput [y] [x]
        = determinantOfMinor( x, y, theMatrix ) * oneOverDeterminant;

        /* (y0,x1)  (y1,x0)  (y1,x2)  and (y2,x1)  all need to be negated. */
      if( 1 == ((x + y) % 2) )
        theOutput [y] [x] = - theOutput [y] [x];
    }

  return true;
}
Run Code Online (Sandbox Code Playgroud)

并使用一些质量较低的测试代码进行完善:

void printMatrix( const double theMatrix [/*Y=*/3] [/*X=*/3] )
{
  for ( int y = 0;  y < 3;  y ++ )
  {
    cout << "[  ";
    for ( int x = 0;  x < 3;  x ++   )
      cout << theMatrix [y] [x] << "  ";
    cout << "]" << endl;
  }
  cout << endl;
}

void matrixMultiply(  const double theMatrixA [/*Y=*/3] [/*X=*/3],
                      const double theMatrixB [/*Y=*/3] [/*X=*/3],
                            double theOutput  [/*Y=*/3] [/*X=*/3]  )
{
  for (   int y = 0;  y < 3;  y ++ )
    for ( int x = 0;  x < 3;  x ++   )
    {
      theOutput [y] [x] = 0;
      for ( int i = 0;  i < 3;  i ++ )
        theOutput [y] [x] +=  theMatrixA [y] [i] * theMatrixB [i] [x];
    }
}

int
main(int argc, char **argv)
{
  if ( argc > 1 )
    SRANDOM( atoi( argv[1] ) );

  double m[3][3] = { { RANDOM_D(0,1e3), RANDOM_D(0,1e3), RANDOM_D(0,1e3) },
                     { RANDOM_D(0,1e3), RANDOM_D(0,1e3), RANDOM_D(0,1e3) },
                     { RANDOM_D(0,1e3), RANDOM_D(0,1e3), RANDOM_D(0,1e3) } };
  double o[3][3], mm[3][3];

  if ( argc <= 2 )
    cout << fixed << setprecision(3);

  printMatrix(m);
  cout << endl << endl;

  SHOW( determinant(m) );
  cout << endl << endl;

  BOUT( inverse(m, o) );
  printMatrix(m);
  printMatrix(o);
  cout << endl << endl;

  matrixMultiply (m, o, mm );
  printMatrix(m);
  printMatrix(o);
  printMatrix(mm);  
  cout << endl << endl;
}
Run Code Online (Sandbox Code Playgroud)

事后:

您可能还想检测非常大的决定因素,因为舍入误差会影响您的准确性!

  • 有趣的是,Ree先生的代码比上面给出的代码更难阅读和理解了几个数量级. (10认同)