我有一个类似这样的算法:
for m = 1 to 2 initialize(work_item(m)) for l = 1 to 2 initialize(work_item(l)) for k = 1 to 2 initialize(work_item(k)) for j = 1 to 2 initialize(work_item(j)) for i = 1 to 2 initialize(work_item(i)) doSomething(work_item(i)) next doSomething(work_item(j)) next doSomething(work_item(k)) next doSomething(work_item(l)) next doSomething(work_item(m)) next
我如何迭代地编写它,使其成为动态的,这样我就不会将自己局限于固定数量的for循环(i,j,k,l,m)(即我可以做(i)或( i,j)或(i,j,k)或(i,j,k,l)等...)?
(我正在用动态的,迭代的解决方案严格寻求答案。如果你不理解这一点,请继续阅读,从上一句开始。)
答案 0 :(得分:9)
完全按照使用递归的方式编写算法,但使用显式堆栈对象而不是递归。即:
var stack = new Stack();
stack.Push(InitialThingy);
while(stack.Count != 0)
{
var currentItem = stack.Pop();
//Do things to current item and add things to stack as we go.
}
答案 1 :(得分:6)
private sub doStuff(depth as integer)
for i as integer = 1 to 2
initialize(work_item(i))
if depth > 1 then doStuff(depth - 1)
doSomething(work_item(i))
next
end sub
比你能提出的任何迭代解决方案更优雅,更短,更难处理。向我倾倒所有你想要的东西,并不是那么真实。
答案 2 :(得分:3)
您可能需要recursion。
答案 3 :(得分:2)
调用变量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]);
}
}
基本上,您正在实现一个二进制计数器,清除所有小于第一个清除位的设置位,然后设置第一个清除位。这将以与给定循环相同的顺序运行do Things。
你可以用不同的范围做类似的事情,每个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
现在,棘手的部分。它首先表明我们在增量之后就开始做东西,但这有两个问题。
(这些基本上是同一个问题)
第一个解决方案很容易,我们只是在主循环之外调用开始的东西。
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。
答案 4 :(得分:2)
我会upvote deinst的解决方案,但我是新的并且没有15个代表...在跟踪每个算法之后,我确实认为deinst具有正确的动态迭代方法。但是,deinst在开始时一次初始化,而roygbiv在循环嵌套中重复初始化。 (我想我之前已经看过deinst的正确编辑。)
答案 5 :(得分:1)
您需要跟踪depth
个变量,其中depth
是您的嵌套深度。您可以使用数组或堆栈,或者您可以使用递归的功能并一次分配单个局部变量
function doEverything(depth) {
if (depth == 0) return;
var i; // a new local variable on each call
for i = 1 to 2 {
initialize(work_item(i));
doEverything(depth-1);
doSomething(work_item(i));
}
}
由于i,j,k,...
的范围是1到2,因此也可以将它们解释为单个整数变量的位。
请注意,嵌套循环总共执行2^depth
次操作。
答案 6 :(得分:1)
void doStuff(int depth)
{
int i[depth];
int sp = depth - 1;
i[sp] = 0;
while (1)
{
if (++i[sp] <= 2) // "next". This is why we init to 0.
{ // At this point, i[sp] is 1 or 2.
// init item i[sp];
if (sp) // If there's space to nest another loop
{
i[--sp] = 0; // Push a 0
continue; // Start iterating with the new 0 as TOS
}
}
else if (++sp >= depth) // Here, i[sp] is 3. Leave the inner loop
break; // and exit if stack's now empty
// use item i[sp];
}
}
仍然比递归更棘手。除非你绝对必须这样做,否则不要使用它。
答案 7 :(得分:0)
你可以构建一个序列集合,其中每个序列是你的循环变量i,j,k,......的排列。
一旦你有了这个集合,你就可以迭代:
foreach (var seq in myCollection)
{
initialize(seq[0]);
initialize(seq[1]);
initialize(seq[2]);
...
doSomething(...);
}
生成序列集合的一种方法,由Eric Lippert提供,通过LINQ:
static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item}));
}
您传递任意长度的序列{{1,2},{1,2},... {1,2}},其中每个内部{1,2}序列替换一个循环变量,并且你会得到一系列值的所有排列。
答案 8 :(得分:0)
您的问题归结为变量n
集数的置换问题的解决方案。这可以通过递归(Python)来解决:
a = ('ab', 'AB', '12')
def permutate(a, s='', nLevel=0):
aResult = []
if nLevel < len(a):
for n in range(0, len(a[nLevel])):
aResult += permutate(a, s + a[nLevel][n], nLevel + 1)
else:
aResult = [s]
return aResult
print(permutate(a))
产生:
['aA1', 'aA2', 'aB1', 'aB2', 'bA1', 'bA2', 'bB1', 'bB2']