Fortran 95 中的行列式
Determinant in Fortran95
此 Fortran 代码使用拉普拉斯公式(未成年人展开)计算 nxn 矩阵的行列式。我完全理解这个过程是如何工作的。
但是有人可以让我深入了解以下代码是如何运行的,比如给定的迭代,这部分代码包含递归函数行列式(矩阵)-假设读入并传递了一些 nxn 矩阵,然后调用辅助因子的另一个函数。我理解代码的某些方面,但它的递归让我深感困惑。我尝试 运行 逐步使用 3x3 矩阵,但无济于事。
! Expansion of determinants using Laplace formula
recursive function determinant(matrix) result(laplace_det)
real, dimension(:,:) :: matrix
integer :: msize(2), i, n
real :: laplace_det, det
real, dimension(:,:), allocatable :: cf
msize = shape(matrix)
n = msize(1)
if (n .eq. 1) then
det = matrix(1,1)
else
det = 0
do i=1, n
allocate(cf(n-1, n-1))
cf = cofactor(matrix, i, 1)
det = det + ((-1)**(i+1))* matrix(i,1) * determinant(cf)
deallocate(cf)
end do
end if
laplace_det = det
end function determinant
function cofactor(matrix, mI, mJ)
real, dimension(:,:) :: matrix
integer :: mI, mJ
integer :: msize(2), i, j, k, l, n
real, dimension(:,:), allocatable :: cofactor
msize = shape(matrix)
n = msize(1)
allocate(cofactor(n-1, n-1))
l=0
k = 1
do i=1, n
if (i .ne. mI) then
l = 1
do j=1, n
if (j .ne. mJ) then
cofactor(k,l) = matrix(i,j)
l = l+ 1
end if
end do
k = k+ 1
end if
end do
return
end function cofactor
我纠结的主要部分是这两个调用和各自辅助因子计算的操作。
cf = cofactor(matrix, i, 1)
det = det + ((-1)**(i+1))* matrix(i,1) * determinant(cf)
任何解释的输入将不胜感激(就像我说的一个迭代的例子)。这是我在 stack-overflow 中的第一个 post,因为我的大部分问题都在 mathstack 中(正如您可能从问题的数学性质中看出的那样)。尽管我确实有编程经验,但递归的概念(尤其是在这个例子中)确实让我感到困惑。
如果需要任何编辑,请继续,我不熟悉堆栈溢出的礼仪。
假设我们将以下 3x3 矩阵传递给 determinant()
:
2 9 4
7 5 3
6 1 8
例程中,为i = 1,2,3
迭代执行以下两行:
cf = cofactor(matrix, i, 1)
det = det + ((-1)**(i+1))* matrix(i,1) * determinant(cf)
对应于第一列的拉普拉斯展开。更具体地说,通过删除 matrix
的第 i
行和第 1 列,将上述 3x3 matrix
传递给 cofactor()
以获得 2x2 子矩阵。得到的2x2的子矩阵(cf
)再传给下一行的determinant()
,计算出这个子矩阵对应的辅因子。因此,在第一次迭代中,我们试图计算
请注意,右侧的三个行列式尚未通过后续调用 determinant() 进行计算。让我们考虑一个这样的后续调用,例如i=1
。我们正在传递以下子矩阵(存储在 cf
)
5 3
1 8
到determinant()
。然后,与父 3x3 矩阵的拉普拉斯展开无关,再次重复上述相同过程。也就是说,determinant() 现在遍历 i=1,2
并尝试计算
请注意,此后续调用中的 i
与先前调用中的 i
不同;它们都是局部变量,存在于例程的特定调用中,并且彼此完全独立。另请注意,虚拟数组参数的索引(如 matrix(:,:)
)在 Fortran 中始终从 1
开始(除非另有说明)。如此反复,直到子矩阵的大小变为1
.
但在实践中,我认为理解这种代码最简单的方法是编写中间数据并跟踪 data/routines 的实际流程。例如,我们可以插入很多print
语句作为
module mymod
implicit none
contains
recursive function determinant(matrix) result(laplace_det)
real :: matrix(:,:)
integer :: i, n, p, q
real :: laplace_det, det
real, allocatable :: cf(:,:)
n = size(matrix, 1)
!***** output *****
print "(a)", "Entering determinant() with matrix = "
do p = 1, n
print "(4x,100(f3.1,x))", ( matrix( p, q ), q=1,n )
enddo
if (n == 1) then
det = matrix(1,1)
else
det = 0
do i = 1, n
allocate( cf(n-1, n-1) )
cf = cofactor( matrix, i, 1 )
!***** output *****
print "(4x,a,i0,a,i0,a)", "Getting a ", &
n-1, "-by-", n-1, " sub-matrix from cofactor():"
do p = 1, n-1
print "(8x, 100(f3.1,x))", ( cf( p, q ), q=1,n-1 )
enddo
print "(4x,a)", "and passing it to determinant()."
det = det + ((-1)**(i+1))* matrix( i, 1 ) * determinant( cf )
deallocate(cf)
end do
end if
laplace_det = det
!***** output *****
print *, " ---> Returning det = ", det
end function
function cofactor(matrix, mI, mJ)
.... (same as the original code)
end function
end module
program main
use mymod
implicit none
real :: a(3,3), det
a( 1, : ) = [ 2.0, 9.0, 4.0 ]
a( 2, : ) = [ 7.0, 5.0, 3.0 ]
a( 3, : ) = [ 6.0, 1.0, 8.0 ]
det = determinant( a )
print "(a, es30.20)", "Final det = ", det
end program
然后输出清楚地显示了数据是如何处理的:
Entering determinant() with matrix =
2.0 9.0 4.0
7.0 5.0 3.0
6.0 1.0 8.0
Getting a 2-by-2 sub-matrix from cofactor():
5.0 3.0
1.0 8.0
and passing it to determinant().
Entering determinant() with matrix =
5.0 3.0
1.0 8.0
Getting a 1-by-1 sub-matrix from cofactor():
8.0
and passing it to determinant().
Entering determinant() with matrix =
8.0
---> Returning det = 8.0000000
Getting a 1-by-1 sub-matrix from cofactor():
3.0
and passing it to determinant().
Entering determinant() with matrix =
3.0
---> Returning det = 3.0000000
---> Returning det = 37.000000
Getting a 2-by-2 sub-matrix from cofactor():
9.0 4.0
1.0 8.0
and passing it to determinant().
Entering determinant() with matrix =
9.0 4.0
1.0 8.0
Getting a 1-by-1 sub-matrix from cofactor():
8.0
and passing it to determinant().
Entering determinant() with matrix =
8.0
---> Returning det = 8.0000000
Getting a 1-by-1 sub-matrix from cofactor():
4.0
and passing it to determinant().
Entering determinant() with matrix =
4.0
---> Returning det = 4.0000000
---> Returning det = 68.000000
Getting a 2-by-2 sub-matrix from cofactor():
9.0 4.0
5.0 3.0
and passing it to determinant().
Entering determinant() with matrix =
9.0 4.0
5.0 3.0
Getting a 1-by-1 sub-matrix from cofactor():
3.0
and passing it to determinant().
Entering determinant() with matrix =
3.0
---> Returning det = 3.0000000
Getting a 1-by-1 sub-matrix from cofactor():
4.0
and passing it to determinant().
Entering determinant() with matrix =
4.0
---> Returning det = 4.0000000
---> Returning det = 7.0000000
---> Returning det = -360.00000
Final det = -3.60000000000000000000E+02
您可以插入更多打印行,直到整个机制变得清晰。
顺便说一句,the Rossetta page 中的代码似乎比 OP 的代码简单得多,直接将子矩阵创建为本地数组。代码的简化版本为
recursive function det_rosetta( mat, n ) result( accum )
integer :: n
real :: mat(n, n)
real :: submat(n-1, n-1), accum
integer :: i, sgn
if ( n == 1 ) then
accum = mat(1,1)
else
accum = 0.0
sgn = 1
do i = 1, n
submat( 1:n-1, 1:i-1 ) = mat( 2:n, 1:i-1 )
submat( 1:n-1, i:n-1 ) = mat( 2:n, i+1:n )
accum = accum + sgn * mat(1, i) * det_rosetta( submat, n-1 )
sgn = - sgn
enddo
endif
end function
请注意,拉普拉斯展开是沿着第一行进行的,并且 submat
是使用数组部分分配的。赋值也可以简单地写成
submat( :, :i-1 ) = mat( 2:, :i-1 )
submat( :, i: ) = mat( 2:, i+1: )
其中省略了数组部分的上下界(那么默认使用上下界的声明值)。后一种形式用于 Rosetta 页面。
此 Fortran 代码使用拉普拉斯公式(未成年人展开)计算 nxn 矩阵的行列式。我完全理解这个过程是如何工作的。
但是有人可以让我深入了解以下代码是如何运行的,比如给定的迭代,这部分代码包含递归函数行列式(矩阵)-假设读入并传递了一些 nxn 矩阵,然后调用辅助因子的另一个函数。我理解代码的某些方面,但它的递归让我深感困惑。我尝试 运行 逐步使用 3x3 矩阵,但无济于事。
! Expansion of determinants using Laplace formula
recursive function determinant(matrix) result(laplace_det)
real, dimension(:,:) :: matrix
integer :: msize(2), i, n
real :: laplace_det, det
real, dimension(:,:), allocatable :: cf
msize = shape(matrix)
n = msize(1)
if (n .eq. 1) then
det = matrix(1,1)
else
det = 0
do i=1, n
allocate(cf(n-1, n-1))
cf = cofactor(matrix, i, 1)
det = det + ((-1)**(i+1))* matrix(i,1) * determinant(cf)
deallocate(cf)
end do
end if
laplace_det = det
end function determinant
function cofactor(matrix, mI, mJ)
real, dimension(:,:) :: matrix
integer :: mI, mJ
integer :: msize(2), i, j, k, l, n
real, dimension(:,:), allocatable :: cofactor
msize = shape(matrix)
n = msize(1)
allocate(cofactor(n-1, n-1))
l=0
k = 1
do i=1, n
if (i .ne. mI) then
l = 1
do j=1, n
if (j .ne. mJ) then
cofactor(k,l) = matrix(i,j)
l = l+ 1
end if
end do
k = k+ 1
end if
end do
return
end function cofactor
我纠结的主要部分是这两个调用和各自辅助因子计算的操作。
cf = cofactor(matrix, i, 1)
det = det + ((-1)**(i+1))* matrix(i,1) * determinant(cf)
任何解释的输入将不胜感激(就像我说的一个迭代的例子)。这是我在 stack-overflow 中的第一个 post,因为我的大部分问题都在 mathstack 中(正如您可能从问题的数学性质中看出的那样)。尽管我确实有编程经验,但递归的概念(尤其是在这个例子中)确实让我感到困惑。
如果需要任何编辑,请继续,我不熟悉堆栈溢出的礼仪。
假设我们将以下 3x3 矩阵传递给 determinant()
:
2 9 4
7 5 3
6 1 8
例程中,为i = 1,2,3
迭代执行以下两行:
cf = cofactor(matrix, i, 1)
det = det + ((-1)**(i+1))* matrix(i,1) * determinant(cf)
对应于第一列的拉普拉斯展开。更具体地说,通过删除 matrix
的第 i
行和第 1 列,将上述 3x3 matrix
传递给 cofactor()
以获得 2x2 子矩阵。得到的2x2的子矩阵(cf
)再传给下一行的determinant()
,计算出这个子矩阵对应的辅因子。因此,在第一次迭代中,我们试图计算
请注意,右侧的三个行列式尚未通过后续调用 determinant() 进行计算。让我们考虑一个这样的后续调用,例如i=1
。我们正在传递以下子矩阵(存储在 cf
)
5 3
1 8
到determinant()
。然后,与父 3x3 矩阵的拉普拉斯展开无关,再次重复上述相同过程。也就是说,determinant() 现在遍历 i=1,2
并尝试计算
请注意,此后续调用中的 i
与先前调用中的 i
不同;它们都是局部变量,存在于例程的特定调用中,并且彼此完全独立。另请注意,虚拟数组参数的索引(如 matrix(:,:)
)在 Fortran 中始终从 1
开始(除非另有说明)。如此反复,直到子矩阵的大小变为1
.
但在实践中,我认为理解这种代码最简单的方法是编写中间数据并跟踪 data/routines 的实际流程。例如,我们可以插入很多print
语句作为
module mymod
implicit none
contains
recursive function determinant(matrix) result(laplace_det)
real :: matrix(:,:)
integer :: i, n, p, q
real :: laplace_det, det
real, allocatable :: cf(:,:)
n = size(matrix, 1)
!***** output *****
print "(a)", "Entering determinant() with matrix = "
do p = 1, n
print "(4x,100(f3.1,x))", ( matrix( p, q ), q=1,n )
enddo
if (n == 1) then
det = matrix(1,1)
else
det = 0
do i = 1, n
allocate( cf(n-1, n-1) )
cf = cofactor( matrix, i, 1 )
!***** output *****
print "(4x,a,i0,a,i0,a)", "Getting a ", &
n-1, "-by-", n-1, " sub-matrix from cofactor():"
do p = 1, n-1
print "(8x, 100(f3.1,x))", ( cf( p, q ), q=1,n-1 )
enddo
print "(4x,a)", "and passing it to determinant()."
det = det + ((-1)**(i+1))* matrix( i, 1 ) * determinant( cf )
deallocate(cf)
end do
end if
laplace_det = det
!***** output *****
print *, " ---> Returning det = ", det
end function
function cofactor(matrix, mI, mJ)
.... (same as the original code)
end function
end module
program main
use mymod
implicit none
real :: a(3,3), det
a( 1, : ) = [ 2.0, 9.0, 4.0 ]
a( 2, : ) = [ 7.0, 5.0, 3.0 ]
a( 3, : ) = [ 6.0, 1.0, 8.0 ]
det = determinant( a )
print "(a, es30.20)", "Final det = ", det
end program
然后输出清楚地显示了数据是如何处理的:
Entering determinant() with matrix =
2.0 9.0 4.0
7.0 5.0 3.0
6.0 1.0 8.0
Getting a 2-by-2 sub-matrix from cofactor():
5.0 3.0
1.0 8.0
and passing it to determinant().
Entering determinant() with matrix =
5.0 3.0
1.0 8.0
Getting a 1-by-1 sub-matrix from cofactor():
8.0
and passing it to determinant().
Entering determinant() with matrix =
8.0
---> Returning det = 8.0000000
Getting a 1-by-1 sub-matrix from cofactor():
3.0
and passing it to determinant().
Entering determinant() with matrix =
3.0
---> Returning det = 3.0000000
---> Returning det = 37.000000
Getting a 2-by-2 sub-matrix from cofactor():
9.0 4.0
1.0 8.0
and passing it to determinant().
Entering determinant() with matrix =
9.0 4.0
1.0 8.0
Getting a 1-by-1 sub-matrix from cofactor():
8.0
and passing it to determinant().
Entering determinant() with matrix =
8.0
---> Returning det = 8.0000000
Getting a 1-by-1 sub-matrix from cofactor():
4.0
and passing it to determinant().
Entering determinant() with matrix =
4.0
---> Returning det = 4.0000000
---> Returning det = 68.000000
Getting a 2-by-2 sub-matrix from cofactor():
9.0 4.0
5.0 3.0
and passing it to determinant().
Entering determinant() with matrix =
9.0 4.0
5.0 3.0
Getting a 1-by-1 sub-matrix from cofactor():
3.0
and passing it to determinant().
Entering determinant() with matrix =
3.0
---> Returning det = 3.0000000
Getting a 1-by-1 sub-matrix from cofactor():
4.0
and passing it to determinant().
Entering determinant() with matrix =
4.0
---> Returning det = 4.0000000
---> Returning det = 7.0000000
---> Returning det = -360.00000
Final det = -3.60000000000000000000E+02
您可以插入更多打印行,直到整个机制变得清晰。
顺便说一句,the Rossetta page 中的代码似乎比 OP 的代码简单得多,直接将子矩阵创建为本地数组。代码的简化版本为
recursive function det_rosetta( mat, n ) result( accum )
integer :: n
real :: mat(n, n)
real :: submat(n-1, n-1), accum
integer :: i, sgn
if ( n == 1 ) then
accum = mat(1,1)
else
accum = 0.0
sgn = 1
do i = 1, n
submat( 1:n-1, 1:i-1 ) = mat( 2:n, 1:i-1 )
submat( 1:n-1, i:n-1 ) = mat( 2:n, i+1:n )
accum = accum + sgn * mat(1, i) * det_rosetta( submat, n-1 )
sgn = - sgn
enddo
endif
end function
请注意,拉普拉斯展开是沿着第一行进行的,并且 submat
是使用数组部分分配的。赋值也可以简单地写成
submat( :, :i-1 ) = mat( 2:, :i-1 )
submat( :, i: ) = mat( 2:, i+1: )
其中省略了数组部分的上下界(那么默认使用上下界的声明值)。后一种形式用于 Rosetta 页面。