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

时间:2017-07-14 07:19:53

标签: fortran integer-arithmetic

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

1 个答案:

答案 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

请注意,在递归调用的进入和退出时,始终保留数组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阶导数的“漂亮印刷”例程(在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