调用变量 i[0] ... i[n] 将它们全部初始化为 1。要在每一步递增
int j, k;
for(j = n - 1; j >= 0; j--){
initialize(i[j]);
}
while (true) {
j = 0;
while (i[j] == 2) {
doSomething(i[j]);
j++;
}
if (j < n) {
doSomething(i[j]);
} else {
break;
}
for (j = 0; j < n; j++) {
if (i[j] == 1) {
i[j] = 2;
break;
}
else {
i[j] = 1;
}
}
if (j == n) break;
for (k = j; k >= 0; k--) {
initialize(i[k]);
}
}
本质上,您正在实现一个二进制计数器,清除所有设置位小于第一个清除位,然后设置第一个清除位。这将以与给定循环相同的顺序运行执行操作。
你可以用不同的范围做类似的事情,每个 i[j] 的范围甚至不必相同。
编辑添加了初始化。
注意 递归在这里可能有点过头了,并且比平面实现的灵活性稍差(考虑从内部循环中止。)。这是一个在实践中经常出现的问题,并不是那么复杂。我们想要做的是让每个循环看起来像
for i = 1 to 2
do beginning stuff with i
do inner loop
do ending stuff with i
next
如果我们只考虑索引变量,我们就会得到看起来像二进制计数器的东西。如果我们在执行最内层循环时查看索引变量的值。
i j k l
1 1 1 1
1 1 1 2
1 1 2 1
实现一个计数器很容易。如果我们只是标记我们的变量,首先是最里面的,这样i -> i[3]、j -> i[2]、k -> i[1] 和l -> [0] 那么单个递增步骤包括
j = 0
while i[j] == 2
i[j] = 1
j++
if j == maxindex
break out, we're done with all the loops
else
i[j] = 2
现在,让我们在循环结束时做一些事情。这很容易,循环结束发生在我们递增之前。所以我们的增量器看起来像
j = 0
while i[j] == 2
do ending stuff with i[j]
i[j] = 1
j++
if j == maxindex
break out, we're done with all the loops
else
do ending stuff with i[j]
i[j] = 2
现在,棘手的部分。乍一看,我们只是在递增之后才开始做一些事情,但这有两个问题。
- 它错过了初始增量(当我们将初始变量设置为 1 时发生的增量
- 它为最终增量中的所有内容调用初始增量例程。
(这些本质上是同一个问题)
第一个的解决方案很简单,我们只是在主循环之外调用开始的东西。
for j = maxindex - 1 downto 0
do beginning stuff with i[j]
while true
j = 0
while i[j] == 2
do ending stuff with i[j]
i[j] = 1
j++
if j == maxindex
break out, we're done with all the loops
else
do ending stuff with i[j]
i[j] = 2
现在要在增加计数后获取其他初始化程序。
for j = maxindex - 1 downto 0
do beginning stuff with i[j]
while true
j = 0
while i[j] == 2
do ending stuff with i[j]
i[j] = 1
j++
if j == maxindex
break out, we're done with all the loops
else
do ending stuff with i[j]
i[j] = 2
for k = j downto 0
do beginning stuff with i[k]
与嵌套循环版本相比,这不会产生任何开销(调用堆栈等)。它可以很容易地从巢穴深处中止。这有点棘手,但如果它比具有显式堆栈的递归版本更复杂,我会感到惊讶。 De gustibus non disputandum est.