Supposons que nous passons la matrice 3x3 suivante determinant()
:
2 9 4
7 5 3
6 1 8
Dans la routine, les deux lignes suivantes sont exécutées itérativement pour i = 1,2,3
:
cf = cofactor(matrix, i, 1)
det = det + ((-1)**(i+1))* matrix(i,1) * determinant(cf)
qui correspond à la Laplace expansion par rapport à la première colonne. Plus précisément, on passe les 3x3 matrix
ci-dessus à cofactor()
pour obtenir une sous-matrice 2x2 en supprimant la 0e ligne i
et la 1ère colonne du matrix
. La sous-matrice 2x2 obtenue (cf
) est ensuite passée à determinant()
dans la ligne suivante pour calculer le cofacteur correspondant à cette sous-matrice. Ainsi, dans ce premier itérations que nous essayons de calculer
Notez ici que les trois déterminants du côté droit ne sont pas encore calculées par des appels ultérieurs de déterminant(). Considérons un tel appel ultérieur, par ex. pour i=1
. Nous passons la sous-matrice suivante (stockée dans cf
)
5 3
1 8
à determinant()
. Ensuite, la même procédure que celle décrite ci-dessus est répétée à nouveau et indépendamment de l'expansion de Laplace pour la matrice mère 3x3. Autrement dit, le déterminant() maintenant itère sur i=1,2
et essaie de calculer
Notez que le i
dans cet appel ultérieur est différent du i
de l'appel précédent; ce sont toutes des variables locales qui vivent à l'intérieur d'un appel particulier d'une routine et sont totalement indépendantes les unes des autres.Notez également que l'index de l'argument du tableau factice (comme matrix(:,:)
) commence toujours par 1
dans Fortran (sauf indication contraire). Ce type d'opérations est répété jusqu'à ce que la taille de la sous-matrice devienne 1
. Mais en pratique, je crois que la façon la plus simple de comprendre ce type de code est d'écrire des données intermédiaires et de suivre le flux réel des données/routines. Par exemple, nous pouvons insérer beaucoup de print
déclarations comme
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
alors la sortie montre clairement comment les données sont traitées:
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
Vous pouvez insérer plusieurs lignes d'impression jusqu'à ce que tout le mécanisme est clair.
BTW, le code the Rossetta page semble beaucoup plus simple que le code de l'OP en créant une sous-matrice directement en tant que tableau local. La version simplifiée du code lit
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
Notez que l'expansion de Laplace est faite le long de la première ligne, et que la submat
est affectée à l'aide des sections de tableau. L'affectation peut aussi être écrit simplement comme
submat(:, :i-1) = mat(2:, :i-1)
submat(:, i:) = mat(2:, i+1:)
où les limites supérieures et inférieures des sections de tableau sont omis (alors, les valeurs déclarées de limites supérieures et inférieures sont utilisées par défaut). Ce dernier formulaire est utilisé dans la page Rosetta.
Il est peut-être utile d'ajouter l'étiquette générale "fortran". Aussi, je me demande lequel des deux suivants est la source de difficulté/confusion: (1) la manipulation des tableaux allouables dans Fortran, (2) le comportement de base des fonctions de récursion dans Fortran (comme illustré par [Fibonacci] (https://www.rosettacode.org/wiki/Fibonacci_sequence#Fortran)). – roygvib
Je comprends l'utilisation de tableaux allouables dans Fortran, pour autant que je sache qu'ils permettent essentiellement une allocation dynamique de la taille du tableau. Bien que je ne puisse pas voir (bien que cela fonctionne clairement) comment la récursion du déterminant (cf) nous donne la valeur correcte à chaque itération récursive. J'ai essayé de suivre le code sur le stylo et le papier pour une seule itération dire 'i = 1' dans le' déterminant de la fonction récursive (matrice) 'mais je me perds dans la fonction du cofacteur. –
Je pense que la fonction 'cofactor()' construit un sous-tableau à partir d'un tableau donné en enlevant la mI-ème ligne et la mJ-ème colonne de la matrice passée, donc 'cf' est un tableau 5x5 si' matrix' est Tableau 6x6, par exemple. Le 'cf' obtenu est ensuite passé à' determinant() 'en tant que' determinant (cf) ', qui sera évalué" fraîchement "(c'est-à-dire, indépendamment de l'appel actuel de determinant()). C'est donc quelque chose comme appeler de plus en plus de fonctions indépendantes avec le même nom. (Désolé si mon explication est mauvaise ...) – roygvib