问题
I'm having trouble figuring out a way to make this algorithm work, as I can't figure out how to do the middle part of the problem. Here's my code so far:
int det(int matrixSize, int matrix[][matrixSize]){
int determinant = 0, matrixValues[matrixSize * matrixSize], matrixFirstRowValues[matrixSize * matrixSize];
for(int i = matrixSize; i > 2; i--){
for(int row = 0; row < matrixSize; row++){
for(int col = 0; col < matrixSize; col++){
matrixFirstRowValues[row + (matrixSize - i)] = matrix[1][col + (matrixSize - i)];
//copies the first row values for an array until we have a 2x2 matrix
}
}
}
//multiply the values in matrix Values by their respective matrix without
//the row and column of these values times -1^row+col
determinant += (matrix[matrixSize-1][matrixSize-1] * matrix[matrixSize-2][matrixSize-2])
- (matrix[matrixSize-1][matrixSize-2] * matrix[matrixSize-2][matrixSize-1]);
return determinant;
}
Being the matrix, a 2-dimensional array with the size of matrixSize, I iterate through it until I'm left with a 2x2 matrix, copying each value of the first row to a new array.
Those values have to be multiplied by the matrix that it's left when I remove the row and column where that value is.
This is the principle of the laplace expansion. The part that's giving me trouble is getting those matrices that are left by removing rows and columns, as I want this to work for a nxn matrix.
Then, in the end the sum that to the det of a 2x2 matrix.
How can I do the middle part (where the comments are) with my current setup?
回答1:
Those values have to be multiplied by the matrix that it's left when I remove the row and column where that value is.
You have to multiply with the cofactor matrix whose entries are the determinants of the matrices that are left over when removing the i-th row and j-th column.
Naturally, this is the setup for a recursive algorithm, since the determinant of the bigger matrix is expressed in terms of the determinants of smaller matrices: if A = (a_{ij})
is the matrix, then det(A) = sum j = 1..n: a_{ij} * det(M_{ij})
, where M_{ij}
is the minor matrix that arises from A when removing the i
-th row and j
-th column where i
is fixed. The base case being the 2-by-2 matrices, maybe also 3-by-3 matrices.
The problem that arises is that an n-by-n
matrix produces n
matrices M_{ij}
of size (n-1)-by-(n-1)
, each of which produces n-1
matrices of size one less and so on until the base case is reached, at which point you'll have to keep track of n!/2
matrices. (It becomes apparent at this point that Laplace expansion is a rather costly algorithm, any algorithm based on Gauss elimination will be far more efficient. But that is just an aside, since we are discussing Laplace expansion.) If done in an iterative fashion, this has to be done manually, a recursive algorithm will have implicit means of bookkeeping via stack frames.
Your approach
Let's have a look at the piece of code that you have provided. It eludes me what precisely you are trying to achieve. Take for instance the statement in the innermost loop which iterates over col
:
matrixFirstRowValues[row + (matrixSize - i)] = matrix[1][col + (matrixSize - i)];
For as long as col
changes in the innermost loop, both row
and i
are fixed, so you are assigning and reassigning from (apparently) the second row in matrix
to the same entry in matrixFirstRowValues
. Not only that, you assign from an index range (matrixSize-i) .. (2*matrixSize - (i+1))
which exceeds the range of the column unless i == matrixSize
, which is only the case for the first value of i
.
As I mentioned before, in the end you do not end up with just one 2-by-2 matrix but n!/2
.
Copying except i-th row and j-th column
Looking at the matrix with i
-th row and j
-th column removed, you end up with four submatrices (some of which may be empty). Restricting yourself to expansion along the first row, then you are dealing with just two submatrices (still some of which may be empty). You may use two loops, one for the matrix to the left of the j
-th column and to the right - or, as suggested in a previous answer - choose to skip the j
-th column using continue
to cycle the loop without updating the target column index. If col
marks the current colum to remove (the current row is always 0), iterate r
over all rows, and c
over all columns and break the column loop in two pieces at c == col
. Let's say, the target matrix is called minor
, then it would look like this:
// expand along first row
for(col = 0; col < matrix_size; col++) {
// copy into minor matrix, left side then right side
for(r = 1; r < matrix_size; r++) {
for(c = 0; c < col; c++) {
minor[r-1][c] = matrix[r][c];
}
for(c = col+1; c < matrix_size; c++) {
minor[r-1][c-1] = matrix[r][c];
}
}
// use "minor" matrix at this point to calculte
// its determinant
}
The index shift r-1
is due to the removal of the first row.
A complete recursive Laplace expansion
As I mentioned before, the Laplace expansion of the determinant lends itself naturally to a recursive algorithm. I will do some changes to your setup, i will not use variable length arrays which are stack allocated, I will instead use heap allocated memory. Since the expansion, if the space is not reused, has an exponential space requirement, the stack might quickly get exhausted already for matrices of moderate size. Consequently, I will need an additional parameter to report back memory allocation failures via an intent out parameter which I call is_valid
.
You will recognise the above matrix copy procedure with a little different names and an additional pointer dereference, since I operate with n-by-n matrices on the heap. I hope it will not lead to too much confusion.
#include <stdio.h>
#include <stdlib.h>
#define SQR(x) ((x)*(x))
int laplace_det(int matrix_size, const int (*mat)[][matrix_size], int *is_valid) {
// base cases
if(matrix_size == 1)
return (*mat)[0][0];
if(matrix_size == 2)
return (*mat)[0][0] * (*mat)[1][1] - (*mat)[1][0] * (*mat)[0][1];
// recusive case, matrix_size > 2
// expansion indiscriminately along the first row
//
// minor matrix with i-th row and j-th column
// removed for the purpose of calculating
// the minor.
// r, c row and column index variables
// col current column in expansion
// d determinant accumulator
//
int r, c, col, d = 0;
int (*minor)[matrix_size-1][matrix_size-1] = calloc(SQR(matrix_size-1), sizeof(int));
if(!minor) {
*is_valid = 0;
return 0;
}
// expand along first row
for(col = 0; col < matrix_size; col++) {
// copy into minor matrix, left side then right side
for(r = 1; r < matrix_size; r++) {
for(c = 0; c < col; c++) {
(*minor)[r-1][c] = (*mat)[r][c];
}
for(c = col+1; c < matrix_size; c++) {
(*minor)[r-1][c-1] = (*mat)[r][c];
}
}
// calculate minor
int temp_d = laplace_det(matrix_size-1, minor, is_valid);
if(!is_valid) {
free(minor);
return 0;
}
d += (col & 1 ? -1 : 1) * (*mat)[0][col] * temp_d;
}
// free resources
free(minor);
return d;
}
Example driver program:
int main(void) {
int is_valid = 1;
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int det_m = laplace_det(3, &matrix, &is_valid);
if(is_valid) {
printf("determinant %d\n", det_m);
}
}
An iterative approach
If you wanted to do the same thing iteratively, you will need to provide space for all n-1
submatrices of smaller size. As the recursive case shows, you can reuse the same space for all submatrices of the same size, but you cannot use that space for matrices of smaller size because each matrix has to spawn all submatrices of size one smaller, one for each column.
Since the original size of the matrix is not known beforehand, traversing all these matrices in a general way is difficult to realise and will require a lot of bookkeeping that we get for free keeping these values in their respective stack frames in the recursive case. But I suppose keeping the current column selector in an array of size matrixSize
, as well as an array of pointers to the submatrices, it will be possible to rewrite this iteratively.
回答2:
I tried to solve the laplace expansion using recursion method. May this help you
Code:
#include <stdio.h>
int determinant(int size,int det[][4]) // size & row of the square matrix
{
int temp[4][4],a=0,b=0,i,j,k;
int sum=0,sign; /* sum will hold value of determinant of the current matrix */
if(size==2)
return (det[0][0]*det[1][1]-det[1][0]*det[0][1]);
sign=1;
for(i=0;i<size;i++) // note that 'i' will indicate column no.
{
a=0;
b=0;
// copy into submatrix and recurse
for(j=1;j<size;j++) // should start from the next row !!
{
for(k=0;k<size;k++)
{
if(k==i) continue;
temp[a][b++]=det[j][k];
}
a++;
b=0;
}
sum+=sign*det[0][i]*determinant(size-1,temp); // increnting row & decrementing size
sign*=-1;
}
return sum;
}
//Main function
int main()
{
int i,j;
int det[4][4] = {{1, 0, 2, -1},
{3, 0, 0, 5},
{2, 1, 4, -3},
{1, 0, 5, 0}
};
printf("%d",determinant(4,det));
}
Cheers!
来源:https://stackoverflow.com/questions/59121487/how-do-i-implement-the-laplace-expansion-algorithm-in-c