diff --git a/math/matrix/determinant.go b/math/matrix/determinant.go new file mode 100644 index 000000000..d2570f8f8 --- /dev/null +++ b/math/matrix/determinant.go @@ -0,0 +1,59 @@ +// determinant.go +// description: This method finds the determinant of a matrix. +// details: For a theoretical explanation as for what the determinant +// represents, see the [Wikipedia Article](https://en.wikipedia.org/wiki/Determinant) +// author [Carter907](https://github.com/Carter907) +// see determinant_test.go + +package matrix + +import ( + "errors" +) + +// Calculates the determinant of the matrix. +// This method only works for square matrices (e.i. matrices with equal rows and columns). +func (mat Matrix[T]) Determinant() (T, error) { + + var determinant T = 0 + var elements = mat.elements + if mat.rows != mat.columns { + + return 0, errors.New("Matrix rows and columns must equal in order to find the determinant.") + } + + // Specify base cases for different sized matrices. + switch mat.rows { + case 1: + return elements[0][0], nil + case 2: + return elements[0][0]*elements[1][1] - elements[1][0]*elements[0][1], nil + default: + for i := 0; i < mat.rows; i++ { + + var initialValue T = 0 + minor := New(mat.rows-1, mat.columns-1, initialValue) + // Fill the contents of minor excluding the 0th row and the ith column. + for j, minor_i := 1, 0; j < mat.rows && minor_i < minor.rows; j, minor_i = j+1, minor_i+1 { + for k, minor_j := 0, 0; k < mat.rows && minor_j < minor.rows; k, minor_j = k+1, minor_j+1 { + if k != i { + minor.elements[minor_i][minor_j] = elements[j][k] + } else { + minor_j-- // Decrement the column of minor to account for skipping the ith column of the matrix. + } + } + } + + if i%2 == 0 { + minor_det, _ := minor.Determinant() + + determinant += elements[0][i] * minor_det + } else { + minor_det, _ := minor.Determinant() + + determinant += elements[0][i] * minor_det + } + } + return determinant, nil + } +} diff --git a/math/matrix/determinant_test.go b/math/matrix/determinant_test.go new file mode 100644 index 000000000..801058ff2 --- /dev/null +++ b/math/matrix/determinant_test.go @@ -0,0 +1,142 @@ +package matrix_test + +import ( + "errors" + "math" + "math/rand" + "testing" + + "github.com/TheAlgorithms/Go/math/matrix" +) + +// Test different matrix contents +func TestMatrixDeterminant(t *testing.T) { + // Find Determinant of a 2 by 2 matrix. + matrix1, err := matrix.NewFromElements([][]int{ + {3, 8}, + {4, 6}, + }) + if err != nil { + t.Fatalf("Error creating 3 by 3 matrix: %v", err) + } + determinant, err := matrix1.Determinant() + if err != nil { + t.Fatalf("Error returned from 3 by 3 matrix: %v", err) + } + if determinant != -14 { + t.Fatalf("Determinant returned for a 3 by 3 matrix was %d; wanted -14", determinant) + } + + // Find Dertminant of a 1 by 1 matrix + expectedValue := rand.Intn(math.MaxInt) + matrix2, err := matrix.NewFromElements([][]int{ + {expectedValue}, + }) + if err != nil { + t.Fatalf("Error creating 1 by 1 matrix: %v", err) + } + determinant, err = matrix2.Determinant() + if err != nil { + t.Fatalf("Error returned from 1 by 1 matrix: %v", err) + } + if determinant != expectedValue { + t.Fatalf("Determinant returned for a 1 by 1 matrix was %d; wanted %d", determinant, expectedValue) + } + +} + +func TestEmptyMatrix(t *testing.T) { + emptyElements := [][]int{} + matrix, err := matrix.NewFromElements(emptyElements) + + if err != nil { + t.Fatalf("Error creating Matrix with empty elements: %v", err) + } + + determinant, err := matrix.Determinant() + + if err != nil { + t.Fatalf("Determinant returned an error for empty matrix: %v", err) + } + + // Check that 0 is returned from an empty matrix. + expectedValue := 0 + if determinant != expectedValue { + t.Errorf("Determinant returned from empty matrix was %d; wanted %d", determinant, expectedValue) + } + +} + +func TestNonSquareMatrix(t *testing.T) { + // Creating non-square matrix for testing. + initialValue := 0 + initialRows := 4 + initialCols := 2 + + nonSquareMatrix := matrix.New(initialRows, initialCols, initialValue) + + determinant, err := nonSquareMatrix.Determinant() + // Check if non square matrix returns an error. + if err == nil { + t.Fatalf("No error was returned for a non-square matrix") + } + + // Check if the correct error was returned. + expectedError := errors.New("Matrix rows and columns must equal in order to find the determinant.") + + if err.Error() != expectedError.Error() { + t.Errorf("Error returned from non-square matrix was \n\"%v\"; \nwanted \n\"%v\"", err, expectedError) + } + + // Check if the determinant of the non-square matrix is 0. + if determinant != 0 { + t.Errorf("Determinant of non-square matrix was not 0 but was %d", determinant) + } + +} + +// Test matrix returned from matrix.New +func TestDefaultMatrix(t *testing.T) { + initialValue := 0 + initialRows := 3 + initialCols := 3 + defaultMatrix := matrix.New(initialRows, initialCols, initialValue) + + determinant, err := defaultMatrix.Determinant() + + if err != nil { + t.Fatalf("Error finding the determinant of 3 by 3 default matrix: %v.", err) + } + expectedValue := 0 + if determinant != expectedValue { + t.Errorf("Determinant of the default matrix with an initial value 0 was %d; wanted %d.", initialValue, expectedValue) + } +} + +// Benchmark a 3 by 3 matrix for computational throughput +func BenchmarkSmallMatrixDeterminant(b *testing.B) { + // Create a 3 by 3 matrix for benchmarking + rows := 3 + columns := 3 + initialValue := 0 + matrix := matrix.New(rows, columns, initialValue) + + for i := 0; i < b.N; i++ { + _, _ = matrix.Determinant() + } +} + +// Benchmark a 10 by 10 matrix for computational throughput. +func BenchmarkMatrixDeterminant(b *testing.B) { + // Create a 10 by 10 matrix for benchmarking + rows := 10 + columns := 10 + initialValue := 0 + matrix := matrix.New(rows, columns, initialValue) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, _ = matrix.Determinant() + } +}