1

I am trying to implement code & algorithms found here:

deteminant of matrix and here: How to calculate matrix determinant? n*n or just 5*5

But I am stuck with it.

My first question is what rule actually this algorithms use (as there are obviously several rules in math by which someone can calculate determinant) - so I would like to check on first place if the algorithm is applied correctly.

My second question is what I done wrong (I mean with implementation) or what is wrong with algorithm itself, as it looks like that for 3x3 and 4x4 it works fine, but for 5x5 it gives NaN. Results are checked with several online matrix determinant calculators, and they are fine except for 5x5.

This is my code:

using System;

public class Matrix
{
    private int row_matrix; //number of rows for matrix
    private int column_matrix; //number of colums for matrix
    private double[,] matrix; //holds values of matrix itself

    //create r*c matrix and fill it with data passed to this constructor
    public Matrix(double[,] double_array)
    {
        matrix = double_array;
        row_matrix = matrix.GetLength(0);
        column_matrix = matrix.GetLength(1);
        Console.WriteLine("Contructor which sets matrix size {0}*{1} and fill it with initial data executed.", row_matrix, column_matrix);
    }

    //returns total number of rows
    public int countRows()
    {
        return row_matrix;
    }

    //returns total number of columns
    public int countColumns()
    {
        return column_matrix;
    }

    //returns value of an element for a given row and column of matrix
    public double readElement(int row, int column)
    {
        return matrix[row, column];
    }

    //sets value of an element for a given row and column of matrix
    public void setElement(double value, int row, int column)
    {
        matrix[row, column] = value;
    }

    public double deterMatrix()
    {
        double det = 0;
        double value = 0;
        int i, j, k;

        i = row_matrix;
        j = column_matrix;
        int n = i;

        if (i != j)
        {
            Console.WriteLine("determinant can be calculated only for sqaure matrix!");
            return det;
        }

        for (i = 0; i < n - 1; i++)
        {
            for (j = i + 1; j < n; j++)
            {
                det = (this.readElement(j, i) / this.readElement(i, i));

                //Console.WriteLine("readElement(j, i): " + this.readElement(j, i));
                //Console.WriteLine("readElement(i, i): " + this.readElement(i, i));
                //Console.WriteLine("det is" + det);
                for (k = i; k < n; k++)
                {
                    value = this.readElement(j, k) - det * this.readElement(i, k);

                    //Console.WriteLine("Set value is:" + value);
                    this.setElement(value, j, k);
                }
            }
        }
        det = 1;
        for (i = 0; i < n; i++)
            det = det * this.readElement(i, i);

        return det;
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        Matrix mat03 = new Matrix(new[,]
        {
            {1.0, 2.0, -1.0},
            {-2.0, -5.0, -1.0},
            {1.0, -1.0, -2.0},
        });

        Matrix mat04 = new Matrix(new[,]
        {
            {1.0, 2.0, 1.0, 3.0},
            {-2.0, -5.0, -2.0, 1.0},
            {1.0, -1.0, -3.0, 2.0},
            {4.0, -1.0, -3.0, 1.0},
        });

        Matrix mat05 = new Matrix(new[,]
        {
            {1.0, 2.0, 1.0, 2.0, 3.0},
            {2.0, 1.0, 2.0, 2.0, 1.0},
            {3.0, 1.0, 3.0, 1.0, 2.0},
            {1.0, 2.0, 4.0, 3.0, 2.0},
            {2.0, 2.0, 1.0, 2.0, 1.0},
        });

        double determinant = mat03.deterMatrix();
        Console.WriteLine("determinant is: {0}", determinant);

        determinant = mat04.deterMatrix();
        Console.WriteLine("determinant is: {0}", determinant);

        determinant = mat05.deterMatrix();
        Console.WriteLine("determinant is: {0}", determinant);
    }
}
Community
  • 1
  • 1
Nenad Bulatović
  • 7,238
  • 14
  • 83
  • 113
  • 1
    Nan is probably due to a `0.0/0.0`. – leppie Mar 20 '13 at 17:16
  • I know it is because of that, or even because of n/0.0, but the question is **why algorithm even comes into that situation**. It should be that it is working algorithm as suggested on other posts (I mentioned them at the very beginning of my question. besides, it works for 3x3 and 4x4. – Nenad Bulatović Mar 20 '13 at 17:25
  • 1
    The algorithm appears to be using Gaussian elimination. But the assumption that the division is valid is false, obviously. My advice is to *correctly implement Gaussian elimination* if that's what you want to do. Likely this algorithm is correct only given certain assumptions; perhaps that the system of equations admits solutions? In any event, I would rewrite it from scratch if I were you. – Eric Lippert Mar 20 '13 at 17:40
  • I agree. However, that means that solutions proposed in other two solutions on links given above are actually FALSE solutions, at least for general case. Correct me if I am wrong? – Nenad Bulatović Mar 20 '13 at 17:46
  • @nenad: I have no idea if the code you grabbed at random off the internet is fit for any particular purpose or not. – Eric Lippert Mar 20 '13 at 20:11

2 Answers2

2

Why re-invent the wheel? A well known method for for getting the determinate AND for inverting a matrix is to do a LU decomposition. In fact, MSDN magazine has recently made a post on how to do this with C# here http://msdn.microsoft.com/en-us/magazine/jj863137.aspx.

The sample code is

Matrix Determinant

With a matrix decomposition method in hand, it’s easy to code a method to compute the determinant of a matrix:

static double MatrixDeterminant(double[][] matrix)
{
  int[] perm;
  int toggle;
  double[][] lum = MatrixDecompose(matrix, out perm, out toggle);
  if (lum == null)
    throw new Exception("Unable to compute MatrixDeterminant");
  double result = toggle;
  for (int i = 0; i < lum.Length; ++i)
    result *= lum[i][i];
  return result;
}

The determinant is evaluated from the product of the diagonals on the decomposed matrix with a sign check. Read the article for more details.

Note that they use a jagged array for a matrix, but you can substitute your own matrix storage translating lum[i][j] into lum[i,j].

Community
  • 1
  • 1
John Alexiou
  • 28,472
  • 11
  • 77
  • 133
  • OK, I am going to implement it and inform you about result. – Nenad Bulatović Mar 20 '13 at 17:46
  • You were right, it's the right way to do it. As you pointed out, that article uses static-method approach and array-of-arrays design for matrices. Because of the other requirements in my complete code I MUST use C# multidimensional array. I done it quite well till now, here is a part I don't understand: double[] rowPtr = result[pRow]; result[pRow] = result[j]; result[j] = rowPtr; I don't know how to "covert it" starting with this: double[] rowPtr = result[pRow]; Even if I do printout with Console.WriteLine(result[pRow]); it writes: System.Double[] – Nenad Bulatović Mar 21 '13 at 22:56
  • 1
    `rowPtr[]` is a 1D array that contains all the elements on a single row of the matrix. You will need a loop over the columns to transfer the values from the 2D array to the 1D `rowPtr` array. `for(j=0; j – John Alexiou Mar 22 '13 at 16:27
1

@ja72 Thank you very much for your instructions. Final solution for calculating any n*n determinant looks like:

using System;

internal class MatrixDecompositionProgram
{
    private static void Main(string[] args)
    {
        float[,] m = MatrixCreate(4, 4);
        m[0, 0] = 3.0f; m[0, 1] = 7.0f; m[0, 2] = 2.0f; m[0, 3] = 5.0f;
        m[1, 0] = 1.0f; m[1, 1] = 8.0f; m[1, 2] = 4.0f; m[1, 3] = 2.0f;
        m[2, 0] = 2.0f; m[2, 1] = 1.0f; m[2, 2] = 9.0f; m[2, 3] = 3.0f;
        m[3, 0] = 5.0f; m[3, 1] = 4.0f; m[3, 2] = 7.0f; m[3, 3] = 1.0f;

        int[] perm;
        int toggle;

        float[,] luMatrix = MatrixDecompose(m, out perm, out toggle);

        float[,] lower = ExtractLower(luMatrix);
        float[,] upper = ExtractUpper(luMatrix);

        float det = MatrixDeterminant(m);

        Console.WriteLine("Determinant of m computed via decomposition = " + det.ToString("F1"));
    }

    // --------------------------------------------------------------------------------------------------------------
    private static float[,] MatrixCreate(int rows, int cols)
    {
        // allocates/creates a matrix initialized to all 0.0. assume rows and cols > 0
        // do error checking here
        float[,] result = new float[rows, cols];
        return result;
    }

    // --------------------------------------------------------------------------------------------------------------
    private static float[,] MatrixDecompose(float[,] matrix, out int[] perm, out int toggle)
    {
        // Doolittle LUP decomposition with partial pivoting.
        // rerturns: result is L (with 1s on diagonal) and U; perm holds row permutations; toggle is +1 or -1 (even or odd)
        int rows = matrix.GetLength(0);
        int cols = matrix.GetLength(1);

        //Check if matrix is square
        if (rows != cols)
            throw new Exception("Attempt to MatrixDecompose a non-square mattrix");

        float[,] result = MatrixDuplicate(matrix); // make a copy of the input matrix

        perm = new int[rows]; // set up row permutation result
        for (int i = 0; i < rows; ++i) { perm[i] = i; } // i are rows counter

        toggle = 1; // toggle tracks row swaps. +1 -> even, -1 -> odd. used by MatrixDeterminant

        for (int j = 0; j < rows - 1; ++j) // each column, j is counter for coulmns
        {
            float colMax = Math.Abs(result[j, j]); // find largest value in col j
            int pRow = j;
            for (int i = j + 1; i < rows; ++i)
            {
                if (result[i, j] > colMax)
                {
                    colMax = result[i, j];
                    pRow = i;
                }
            }

            if (pRow != j) // if largest value not on pivot, swap rows
            {
                float[] rowPtr = new float[result.GetLength(1)];

                //in order to preserve value of j new variable k for counter is declared
                //rowPtr[] is a 1D array that contains all the elements on a single row of the matrix
                //there has to be a loop over the columns to transfer the values
                //from the 2D array to the 1D rowPtr array.
                //----tranfer 2D array to 1D array BEGIN

                for (int k = 0; k < result.GetLength(1); k++)
                {
                    rowPtr[k] = result[pRow, k];
                }

                for (int k = 0; k < result.GetLength(1); k++)
                {
                    result[pRow, k] = result[j, k];
                }

                for (int k = 0; k < result.GetLength(1); k++)
                {
                    result[j, k] = rowPtr[k];
                }

                //----tranfer 2D array to 1D array END

                int tmp = perm[pRow]; // and swap perm info
                perm[pRow] = perm[j];
                perm[j] = tmp;

                toggle = -toggle; // adjust the row-swap toggle
            }

            if (Math.Abs(result[j, j]) < 1.0E-20) // if diagonal after swap is zero . . .
                return null; // consider a throw

            for (int i = j + 1; i < rows; ++i)
            {
                result[i, j] /= result[j, j];
                for (int k = j + 1; k < rows; ++k)
                {
                    result[i, k] -= result[i, j] * result[j, k];
                }
            }
        } // main j column loop

        return result;
    } // MatrixDecompose

    // --------------------------------------------------------------------------------------------------------------
    private static float MatrixDeterminant(float[,] matrix)
    {
        int[] perm;
        int toggle;
        float[,] lum = MatrixDecompose(matrix, out perm, out toggle);
        if (lum == null)
            throw new Exception("Unable to compute MatrixDeterminant");
        float result = toggle;
        for (int i = 0; i < lum.GetLength(0); ++i)
            result *= lum[i, i];

        return result;
    }

    // --------------------------------------------------------------------------------------------------------------
    private static float[,] MatrixDuplicate(float[,] matrix)
    {
        // allocates/creates a duplicate of a matrix. assumes matrix is not null.
        float[,] result = MatrixCreate(matrix.GetLength(0), matrix.GetLength(1));
        for (int i = 0; i < matrix.GetLength(0); ++i) // copy the values
            for (int j = 0; j < matrix.GetLength(1); ++j)
                result[i, j] = matrix[i, j];
        return result;
    }

    // --------------------------------------------------------------------------------------------------------------
    private static float[,] ExtractLower(float[,] matrix)
    {
        // lower part of a Doolittle decomposition (1.0s on diagonal, 0.0s in upper)
        int rows = matrix.GetLength(0); int cols = matrix.GetLength(1);
        float[,] result = MatrixCreate(rows, cols);
        for (int i = 0; i < rows; ++i)
        {
            for (int j = 0; j < cols; ++j)
            {
                if (i == j)
                    result[i, j] = 1.0f;
                else if (i > j)
                    result[i, j] = matrix[i, j];
            }
        }
        return result;
    }

    // --------------------------------------------------------------------------------------------------------------
    private static float[,] ExtractUpper(float[,] matrix)
    {
        // upper part of a Doolittle decomposition (0.0s in the strictly lower part)
        int rows = matrix.GetLength(0); int cols = matrix.GetLength(1);
        float[,] result = MatrixCreate(rows, cols);
        for (int i = 0; i < rows; ++i)
        {
            for (int j = 0; j < cols; ++j)
            {
                if (i <= j)
                    result[i, j] = matrix[i, j];
            }
        }
        return result;
    }
}
Nenad Bulatović
  • 7,238
  • 14
  • 83
  • 113