我遇到了fortran 90代码的问题。所以,我有n+1
个正半正整数k1
,k2
,k3
,...,kn
和n
。现在,对于给定的n
,我需要查找k1
,k2
,k3
,...,kn
的所有可能组合,以便{{1} }}。有人可能会考虑使用n级嵌套循环,每个循环用于k1+2*k2+3*k3+...+n*kn = n
,从ki
到0
,但实际上我会将此代码放在子例程中n
(即n
的数量)将是此子例程的输入。因此,如果我使用嵌套循环,那么嵌套级别的数量应该是自动化的,我认为与Fortran相比很难(如果不是不可能)。有没有更好的方法来解决这个问题?
答案 0 :(得分:2)
如果n
相当小(比如5),我认为编写多维循环以获得满足k1 +的所需k向量(k1,k2,...,kn)更简单k2 * 2 + ... + kn * n = n。否则,它可能是使用递归的选项。示例代码可能如下所示(请注意,我们需要recursive
关键字):
module recur_mod
implicit none
integer, parameter :: nvecmax = 1000
contains
subroutine recur_main( n, kveclist, nvec )
integer, intent(in) :: n
integer, allocatable, intent(out) :: kveclist(:,:) !! list of k-vectors
integer, intent(out) :: nvec !! number of k-vectors
integer kvec( n ), ind
allocate( kveclist( n, nvecmax ) )
kveclist(:,:) = 0
nvec = 0
ind = 1
kvec(:) = 0
call recur_sub( n, ind, kvec, kveclist, nvec ) !! now entering recursion...
endsubroutine
recursive subroutine recur_sub( n, ind, kvec, kveclist, nvec )
integer, intent(in) :: n, ind
integer, intent(inout) :: kvec(:), kveclist(:,:), nvec
integer k, ksum, t, ind_next
do k = 0, n
kvec( ind ) = k
ksum = sum( [( kvec( t ) * t, t = 1, ind )] ) !! k1 + k2*2 + ... + ki*i
if ( ksum > n ) cycle !! early rejection
if ( ind < n ) then
ind_next = ind + 1
call recur_sub( n, ind_next, kvec, kveclist, nvec ) !! go to the next index
endif
if ( ind == n .and. ksum == n ) then
nvec = nvec + 1
if ( nvec > nvecmax ) stop "nvecmax too small"
kveclist( :, nvec ) = kvec(:) !! save k-vectors
endif
enddo
endsubroutine
end
program main
use recur_mod
implicit none
integer, allocatable :: kveclist(:,:)
integer :: n, nvec, ivec
do n = 1, 4
call recur_main( n, kveclist, nvec )
print *
print *, "For n = ", n
do ivec = 1, nvec
print *, "kvec = ", kveclist( :, ivec )
enddo
enddo
end
给出(使用gfortran 7.1)
For n = 1
kvec = 1
For n = 2
kvec = 0 1
kvec = 2 0
For n = 3
kvec = 0 0 1
kvec = 1 1 0
kvec = 3 0 0
For n = 4
kvec = 0 0 0 1
kvec = 0 2 0 0
kvec = 1 0 1 0
kvec = 2 1 0 0
kvec = 4 0 0 0
在这里,我们可以看到,例如,n = 4的kvec = [k1,k2,k3,k4] = [2,1,0,0]
满足k1 + k2*2 + k3*3 + k4*4 = 2 + 1*2 + 0 + 0 = 4
。使用这些k向量,我们可以评估f(g(x))的n次导数(如OP所述,遵循此page)。
要了解递归的工作原理,插入很多内容通常很有用
print
个语句并监视变量的变化。例如,代码的“详细”版本可能看起来像这样(在这里,为了简单起见,我删除了早期的拒绝事项):
recursive subroutine recur_sub( n, ind, kvec, kveclist, nvec )
integer, intent(in) :: n, ind
integer, intent(inout) :: kvec(:), kveclist(:,:), nvec
integer k, ksum, t, ind_next
print *, "Top of recur_sub: ind = ", ind
do k = 0, n
kvec( ind ) = k
print *, "ind = ", ind, " k = ", k, "kvec = ", kvec
if ( ind < n ) then
ind_next = ind + 1
print *, " > now going to the next level"
call recur_sub( n, ind_next, kvec, kveclist, nvec )
print *, " > returned to the current level"
endif
if ( ind == n ) then
ksum = sum( [( kvec( t ) * t, t = 1, n )] )
if ( ksum == n ) then
nvec = nvec + 1
if ( nvec > nvecmax ) stop "nvecmax too small"
kveclist( :, nvec ) = kvec(:)
endif
endif
enddo
print *, "Exiting recur_sub"
endsubroutine
赋予n = 2
:
Top of recur_sub: ind = 1
ind = 1 k = 0 kvec = 0 0
> now going to the next level
Top of recur_sub: ind = 2
ind = 2 k = 0 kvec = 0 0
ind = 2 k = 1 kvec = 0 1
ind = 2 k = 2 kvec = 0 2
Exiting recur_sub
> returned to the current level
ind = 1 k = 1 kvec = 1 2
> now going to the next level
Top of recur_sub: ind = 2
ind = 2 k = 0 kvec = 1 0
ind = 2 k = 1 kvec = 1 1
ind = 2 k = 2 kvec = 1 2
Exiting recur_sub
> returned to the current level
ind = 1 k = 2 kvec = 2 2
> now going to the next level
Top of recur_sub: ind = 2
ind = 2 k = 0 kvec = 2 0
ind = 2 k = 1 kvec = 2 1
ind = 2 k = 2 kvec = 2 2
Exiting recur_sub
> returned to the current level
Exiting recur_sub
For n = 2
kvec = 0 1
kvec = 2 0
请注意,在递归调用的进入和退出时,始终保留数组kvec
和kveclist
的值。特别是,我们为每个维度覆盖kvec
的元素,以获得可能模式的完整列表(因此基本上等同于多维循环)。我们还注意到kvec
的值仅在最终递归级别(即ind = n
)才有意义。
另一种获得工作的方法是重写对特定n
的等效,非递归调用的递归调用。例如,n = 2
的情况可以如下重写。这不依赖于递归,而是执行与上面代码相同的操作(对于n = 2
)。如果我们想象recur_sub2
内嵌到recur_sub
,很明显这些例程相当于k1
和k2
上的二维循环。
!! routine specific for n = 2
subroutine recur_sub( n, ind, kvec, kveclist, nvec )
integer, intent(in) :: n, ind
integer, intent(inout) :: kvec(:), kveclist(:,:), nvec
integer k1
!! now ind == 1
do k1 = 0, n
kvec( ind ) = k1
call recur_sub2( n, ind + 1, kvec, kveclist, nvec ) !! go to next index
enddo
endsubroutine
!! routine specific for n = 2
subroutine recur_sub2( n, ind, kvec, kveclist, nvec )
integer, intent(in) :: n, ind
integer, intent(inout) :: kvec(:), kveclist(:,:), nvec
integer k2, ksum, t
!! now ind == n == 2
do k2 = 0, n
kvec( ind ) = k2
ksum = sum( [( kvec( t ) * t, t = 1, n )] )
if ( ksum == 2 ) then
nvec = nvec + 1
if ( nvec > nvecmax ) stop "nvecmax too small"
kveclist( :, nvec ) = kvec(:) !! save k-vectors
endif
enddo
endsubroutine
附录
以下是f(g(x))的n阶导数的“漂亮印刷”例程(在Julia中)(仅仅是为了好玩)。如有必要,请在Fortran中制作相应的例程(但请注意大n!)
function recur_main( n )
kvec = zeros( Int, n ) # [k1,k2,...,kn] (work vector)
kveclist = Vector{Int}[] # list of k-vectors (output)
recur_sub( n, 1, kvec, kveclist ) # now entering recursion over {ki}...
return kveclist
end
function recur_sub( n, i, kvec, kveclist )
for ki = 0 : n
kvec[ i ] = ki
ksum = sum( kvec[ t ] * t for t = 1:i ) # k1 + k2*2 + ... + ki*i
ksum > n && continue # early rejection
if i < n
recur_sub( n, i + 1, kvec, kveclist ) # go to the next index
end
if i == n && ksum == n
push!( kveclist, copy( kvec ) ) # save k-vectors
end
end
end
function showderiv( n )
kveclist = recur_main( n )
println()
println( "(f(g))_$(n) = " )
fact( k ) = factorial( big(k) )
for (term, kvec) in enumerate( kveclist )
fac1 = div( fact( n ), prod( fact( kvec[ i ] ) for i = 1:n ) )
fac2 = prod( fact( i )^kvec[ i ] for i = 1:n )
coeff = div( fac1, fac2 )
term == 1 ? print( " " ) : print( " + " )
coeff == 1 ? print( " " ^ 15 ) : @printf( "%15i", coeff )
print( " (f$( sum( kvec ) ))" )
for i = 1 : length( kvec )
ki = kvec[ i ]
if ki > 0
print( "(g$( i ))" )
ki > 1 && print( "^$( ki )" )
end
end
println()
end
end
#--- test ---
if false
for n = 1 : 4
kveclist = recur_main( n )
println( "\nFor n = ", n )
for kvec in kveclist
println( "kvec = ", kvec )
end
end
end
showderiv( 1 )
showderiv( 2 )
showderiv( 3 )
showderiv( 4 )
showderiv( 5 )
showderiv( 8 )
showderiv( 10 )
结果(其中fm
和gm
分别代表f
和g
的第m个衍生产品):
(f(g))_1 =
(f1)(g1)
(f(g))_2 =
(f1)(g2)
+ (f2)(g1)^2
(f(g))_3 =
(f1)(g3)
+ 3 (f2)(g1)(g2)
+ (f3)(g1)^3
(f(g))_4 =
(f1)(g4)
+ 3 (f2)(g2)^2
+ 4 (f2)(g1)(g3)
+ 6 (f3)(g1)^2(g2)
+ (f4)(g1)^4
(f(g))_5 =
(f1)(g5)
+ 10 (f2)(g2)(g3)
+ 5 (f2)(g1)(g4)
+ 15 (f3)(g1)(g2)^2
+ 10 (f3)(g1)^2(g3)
+ 10 (f4)(g1)^3(g2)
+ (f5)(g1)^5
(f(g))_8 =
(f1)(g8)
+ 35 (f2)(g4)^2
+ 56 (f2)(g3)(g5)
+ 28 (f2)(g2)(g6)
+ 280 (f3)(g2)(g3)^2
+ 210 (f3)(g2)^2(g4)
+ 105 (f4)(g2)^4
+ 8 (f2)(g1)(g7)
+ 280 (f3)(g1)(g3)(g4)
+ 168 (f3)(g1)(g2)(g5)
+ 840 (f4)(g1)(g2)^2(g3)
+ 28 (f3)(g1)^2(g6)
+ 280 (f4)(g1)^2(g3)^2
+ 420 (f4)(g1)^2(g2)(g4)
+ 420 (f5)(g1)^2(g2)^3
+ 56 (f4)(g1)^3(g5)
+ 560 (f5)(g1)^3(g2)(g3)
+ 70 (f5)(g1)^4(g4)
+ 210 (f6)(g1)^4(g2)^2
+ 56 (f6)(g1)^5(g3)
+ 28 (f7)(g1)^6(g2)
+ (f8)(g1)^8
(f(g))_10 =
(f1)(g10)
+ 126 (f2)(g5)^2
+ 210 (f2)(g4)(g6)
+ 120 (f2)(g3)(g7)
+ 2100 (f3)(g3)^2(g4)
+ 45 (f2)(g2)(g8)
+ 1575 (f3)(g2)(g4)^2
+ 2520 (f3)(g2)(g3)(g5)
+ 630 (f3)(g2)^2(g6)
+ 6300 (f4)(g2)^2(g3)^2
+ 3150 (f4)(g2)^3(g4)
+ 945 (f5)(g2)^5
+ 10 (f2)(g1)(g9)
+ 1260 (f3)(g1)(g4)(g5)
+ 840 (f3)(g1)(g3)(g6)
+ 2800 (f4)(g1)(g3)^3
+ 360 (f3)(g1)(g2)(g7)
+ 12600 (f4)(g1)(g2)(g3)(g4)
+ 3780 (f4)(g1)(g2)^2(g5)
+ 12600 (f5)(g1)(g2)^3(g3)
+ 45 (f3)(g1)^2(g8)
+ 1575 (f4)(g1)^2(g4)^2
+ 2520 (f4)(g1)^2(g3)(g5)
+ 1260 (f4)(g1)^2(g2)(g6)
+ 12600 (f5)(g1)^2(g2)(g3)^2
+ 9450 (f5)(g1)^2(g2)^2(g4)
+ 4725 (f6)(g1)^2(g2)^4
+ 120 (f4)(g1)^3(g7)
+ 4200 (f5)(g1)^3(g3)(g4)
+ 2520 (f5)(g1)^3(g2)(g5)
+ 12600 (f6)(g1)^3(g2)^2(g3)
+ 210 (f5)(g1)^4(g6)
+ 2100 (f6)(g1)^4(g3)^2
+ 3150 (f6)(g1)^4(g2)(g4)
+ 3150 (f7)(g1)^4(g2)^3
+ 252 (f6)(g1)^5(g5)
+ 2520 (f7)(g1)^5(g2)(g3)
+ 210 (f7)(g1)^6(g4)
+ 630 (f8)(g1)^6(g2)^2
+ 120 (f8)(g1)^7(g3)
+ 45 (f9)(g1)^8(g2)
+ (f10)(g1)^10