-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add determinant implementation for matrix (#732)
* feat: add determinant implementation for matrix Added the Determinant method for the Matrix struct under math/matrix. This method returns the determinant of the matrix. * fix: determinant spelling and linting Fixed the spelling error in determinant.go. Fixed ineffectual error assignment in determinant_test.go. * test: add test for matrix determinant Added a test case for single-element matrix. --------- Co-authored-by: Rak Laptudirm <[email protected]>
- Loading branch information
1 parent
662e8e9
commit 24c7f1f
Showing
2 changed files
with
201 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
} | ||
} |