找到所有可能的半正定整数,其加权和等于某个整数

Find all possible positive semi definite integers whose weighted sum is equal certain integer

我在使用 Fortran 90 代码时遇到问题。所以,我有 n+1 个半正定整数 k1k2k3、...、knn。现在对于给定的 n,我需要找到 k1k2k3、...、kn 的所有可能组合,使得 k1+2*k2+3*k3+...+n*kn = n.人们可能会想到使用 n 级嵌套循环,每个 for ki0 运行到 n 但实际上我会将这段代码放在子例程中 n (即 k 的数量)将作为该子程序的输入。因此,如果我要使用嵌套循环,那么嵌套级别的数量应该是自动化的,我认为这很难(如果不是不可能)用 Fortran 实现。有没有更好的方法来解决这个问题?

如果n相当小(比如说,5),我认为编写多维循环以获得满足的所需k向量(k1,k2,...,kn)会更简单k1 + 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

这里,我们可以看出,例如kvec = [k1,k2,k3,k4] = [2,1,0,0] for n=4 满足k1 + k2*2 + k3*3 + k4*4 = 2 + 1*2 + 0 + 0 = 4。使用这些 k 向量,我们可以评估 f(g(x)) 的 n 阶导数(如 OP 所述,在 page 之后)。


要看递归是如何工作的,插入很多通常很有用 print 语句并监视变量如何变化。例如,代码的 "verbose" 版本可能如下所示(此处,为简单起见,我删除了早期拒绝的内容):

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

请注意,数组 kveckveclist 的值始终在递归调用的进入和退出时保留。特别是,我们正在为每个维度覆盖 kvec 的元素以获得可能模式的完整列表(因此本质上等同于多维循环)。我们还注意到 kvec 的值仅在最终递归级别(即 ind = n)才有意义。


获得工作的另一种方法是将递归调用重写为特定 n 的等效非递归调用。例如,n = 2的情况可以改写如下。这不依赖于递归,而是执行与上述代码相同的操作(对于 n = 2)。如果我们想象将 recur_sub2 内联到 recur_sub,很明显这些例程等效于 k1k2.

上的二维循环
!! 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 阶导数的 "pretty print" 例程(在 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 )

结果(其中fmgm分别表示fg的第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