【问题标题】:How to make recursive nested loops which use loop variables inside?如何制作内部使用循环变量的递归嵌套循环?
【发布时间】:2013-08-19 02:03:06
【问题描述】:

我需要创建一个任意深度的嵌套循环。递归循环似乎是正确的方法,但我不知道如何在循环中使用循环变量。例如,一旦我将深度指定为 3,它应该像

count = 1 
for i=1, Nmax-2
    for j=i+1, Nmax-1
        for k=j+1,Nmax
            function(i,j,k,0,0,0,0....) // a function having Nmax arguments
            count += 1
        end
    end
end

我想做一个以循环深度为参数的子程序。

更新:

我实施了 Zoltan 提出的方案。为了简单起见,我用python编写了它。

count = 0;

def f(CurrentDepth, ArgSoFar, MaxDepth, Nmax): 
    global count;
    if CurrentDepth > MaxDepth:
        count += 1;
        print count, ArgSoFar;
    else:
        if CurrentDepth == 1:
            for i in range(1, Nmax + 2 - MaxDepth):
                NewArgs = ArgSoFar;
                NewArgs[1-1] = i;
                f(2, NewArgs, MaxDepth, Nmax);
        else:
            for i in range(ArgSoFar[CurrentDepth-1-1] + 1, Nmax + CurrentDepth - MaxDepth +1):
                NewArgs = ArgSoFar;
                NewArgs[CurrentDepth-1] = i;
                f(CurrentDepth + 1, NewArgs, MaxDepth, Nmax);

f(1,[0,0,0,0,0],3,5)

结果是

1 [1, 2, 3, 0, 0]
2 [1, 2, 4, 0, 0]
3 [1, 2, 5, 0, 0]
4 [1, 3, 4, 0, 0]
5 [1, 3, 5, 0, 0]
6 [1, 4, 5, 0, 0]
7 [2, 3, 4, 0, 0]
8 [2, 3, 5, 0, 0]
9 [2, 4, 5, 0, 0]
10 [3, 4, 5, 0, 0] 

可能有更好的方法来做到这一点,但到目前为止,这个方法工作正常。在 fortran 中执行此操作似乎很容易。非常感谢您的帮助!!!

【问题讨论】:

  • 你能标记语言吗?使用 C++ 模板可能会派上用场。对于 Java,您可能应该使用列表。等等。
  • 附言。我正在使用 fortran90。

标签: recursion fortran nested-loops


【解决方案1】:

这是您可以做您想做的事情的一种方法。这是伪代码,我还没有写足够的代码来编译和测试它,但你应该得到图片。

定义一个函数,我们称它为fun1,它接受除其他外一个整数数组参数,也许像这样

<type> function fun1(indices, other_arguments)
    integer, dimension(:), intent(in) :: indices
    ...

你可以这样称呼

fun1([4,5,6],...)

对此的解释是,该功能是使用像这样深的3层循环嵌套:

do ix = 1,4
    do jx = 1,5
        do kx = 1,6
        ...

当然,您不能编写深度在运行时确定的循环嵌套(无论如何都不是在 Fortran 中),因此您可以按照

的行将其扁平化为单个循环
do ix = 1, product(indices)

如果您需要循环内各个索引的值,您需要unflatten线性化索引。请注意,您所做的只是编写代码以将数组索引从 N-D 转换为 1-D,反之亦然;当您可以在编译时指定数组的等级时,这就是编译器为您做的事情。如果内部循环不运行在整个索引范围内,您将不得不做一些更复杂的事情,需要仔细编码但并不困难。

根据您实际尝试执行的操作,这可能是也可能不是一个好的甚至令人满意的方法。如果您尝试编写一个函数来计算数组中每个元素的值,当您编写该函数时,该数组中的秩未知,那么前面的建议是完全错误的,在这种情况下,您需要编写一个 elemental 函数.如果您想了解更多信息,请更新您的问题。

【讨论】:

  • 感谢您的解决方案。很高兴知道总是有多种方法可以做同样的事情!
【解决方案2】:

您可以将函数定义为具有 List 参数,该参数最初为空

void f(int num,List argumentsSoFar){

  // call f() for num+1..Nmax
  for(i = num+1 ; i < Nmax ; i++){
    List newArgs=argumentsSoFar.clone();
    newArgs.add(i);
    f(i,newArgs);
  }
  if (num+1==Nmax){
     // do the work with your argument list...i think you wanted to arrive here ;)
  }

}

警告:堆栈应该能够处理Nmax 深度函数调用

【讨论】:

  • 谢谢你,佐尔坦!我需要更多时间以 fortran 方式思考这个例程。对不起,我没有放语言标签...
【解决方案3】:

实现您想要的另一种方法是基于高性能标记的答案,但可以更通用:

subroutine nestedLoop(indicesIn)

  ! Input indices, of arbitrary rank
  integer,dimension(:),intent(in) :: indicesIn

  ! Internal indices, here set to length 5 for brevity, but set as many as you'd like
  integer,dimension(5) :: indices = 0

  integer :: i1,i2,i3,i4,i5

  indices(1:size(indicesIn)) = indicesIn

  do i1 = 0,indices(1)
    do i2 = 0,indices(2)
      do i3 = 0,indices(3)
        do i4 = 0,indices(4)
          do i5 = 0,indices(5)

            ! Do calculations here:
            ! myFunc(i1,i2,i3,i4,i5)

          enddo
        enddo
      enddo
    enddo
  enddo

endsubroutine nestedLoop

您现在已经明确编码了嵌套循环,但除非另有需要,否则这些都是 1-trip 循环。请注意,如果您打算构建取决于嵌套循环深度的秩数组,则可以上升到 7 的秩,如果您有支持它的编译器,则可以上升到 15(Fortran 2008)。您现在可以尝试:

call nestedLoop([1])
call nestedLoop([2,3])
call nestedLoop([1,2,3,2,1])

您可以根据自己的喜好和期望的适用性修改此例程,添加异常处理等。

【讨论】:

  • 感谢您的代码,并指出我没有考虑到的限制。我很好奇递归循环是否被限制为 7 次或 15 次。
  • 对不起,我太傻了。最大排名限制实际上仅适用于数组。我现在看到您打算调用具有一定数量参数的函数或子例程,因此最大等级限制不适用于那里。我会相应地编辑我的答案。
【解决方案4】:

从 OOP 方法来看,每个循环都可以由一个“循环”对象表示——这个对象可以在包含其自身的另一个实例的同时被构造。然后,理论上你可以将它们嵌套到你需要的深度。

Loop1 将执行 Loop2 将执行 Loop3.. 并以此类推。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-06
    • 2014-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多