Simple 3x3 matrix inverse code (C++)

后端 未结 13 2084
花落未央
花落未央 2020-12-04 19:35

What\'s the easiest way to compute a 3x3 matrix inverse?

I\'m just looking for a short code snippet that\'ll do the trick for non-singular matrices, possibly using C

13条回答
  •  天命终不由人
    2020-12-04 20:05

    With all due respect to our unknown (yahoo) poster, I look at code like that and just die a little inside. Alphabet soup is just so insanely difficult to debug. A single typo anywhere in there can really ruin your whole day. Sadly, this particular example lacked variables with underscores. It's so much more fun when we have a_b-c_d*e_f-g_h. Especially when using a font where _ and - have the same pixel length.

    Taking up Suvesh Pratapa on his suggestion, I note:

    Given 3x3 matrix:
           y0x0  y0x1  y0x2
           y1x0  y1x1  y1x2
           y2x0  y2x1  y2x2
    Declared as double matrix [/*Y=*/3] [/*X=*/3];
    

    (A) When taking a minor of a 3x3 array, we have 4 values of interest. The lower X/Y index is always 0 or 1. The higher X/Y index is always 1 or 2. Always! Therefore:

    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] );
    }
    

    (B) Determinant is now: (Note the minus sign!)

    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 ) );
    }
    

    (C) And the inverse is now:

    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;
    }
    

    And round it out with a little lower-quality testing code:

    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;
    }
    

    Afterthought:

    You may also want to detect very large determinants as round-off errors will affect your accuracy!

提交回复
热议问题