views:

350

answers:

8

I have an algorithm that goes something like this:

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

How can I write this iteratively, making it dynamic, such that I don't limit myself to a fixed number of for loops (i, j, k, l, m) (i.e. I can do (i) or (i, j) or (i, j, k) or (i, j, k, l) etc...)?

(I am strictly seeking answers with dynamic, iterative solutions. If you do not understand this, please continue reading, starting with the previous sentence.)

+3  A: 

You probably want recursion.

Jonathan Grynspan
Well you already have a iterative solution, hence why @Jonathan suggest recursion.
Nathan W
If there's a particular reason why recursion won't work for you, please state it. This is precisely the sort of problem that recursion solves best. Any nominally iterative solution is still going to be recursive; it'll just end up substituting a stack structure (such as a C++ `vector`) in place of the call stack.
Jonathan Grynspan
+2  A: 

Call the variables i[0] ... i[n] initialize them all to 1. To increment at each step do

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]);
  }
}

Essentially you are implementing a binary counter clearing all the set bits less than the first clear bit, then setting the first clear bit. This runs the do somethings in the same order as the given loop.

You can do similar things with different ranges, the ranges of each i[j] need not even be the same.

Edit added initialization.

Note Recursion is probably overkill here, and is somewhat less flexible than the flat implementation (consider aborting from the inner loop.). This is a problem that comes up often in practice, and is not all that complicated. What we want to do is to have each loop look like

for i = 1 to 2
   do beginning stuff with i
   do inner loop
   do ending stuff with i
next

If we just consider the index variables we have what looks like a binary counter. If we look at the values of the index variables when we perform the innermost loop.

 i j k l
 1 1 1 1
 1 1 1 2
 1 1 2 1

implementing a counter is easy. If we just label our variables, innermost first, so that i -> i[3], j -> i[2], k -> i[1] and l -> [0] then a single incrementing step consists of

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

now, let us do the stuff at the end of the loop. This is easy, the end of the loop happens just before we increment. So our incrementer looks like

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

Now, the tricky part. It at first appears that we do the beginning stuff just after incrementing, but this has two problems.

  1. It misses the initial increments (the ones that happen when we set the initial variables to 1
  2. It calls inital increment routines for everything on the final increment.

(these are essentially the same problem)

the solution to the first is easy, we just call beginning stuff outside the main loop.

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

Now to get the other initializers we do them after we have incremented the count.

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]

This takes no overhead (call stack, etc) over the nested loop version. It makes it easy to abort from deep in the nest. It is a bit tricky, but I'd be surprised if it is more complicated than the recursive version with an explicit stack. De gustibus non disputandum est.

deinst
@roygbiv Thanks. I would say, however, that you should have made your aversion to recursion clearer in the statement of the question. For this particular question, I do not think that recursion is a particularly good solution, but I am obviously in the minority.
deinst
This really is a pretty good post, for what it is. Unfortunate that what it is is a description of an approach so convoluted it takes 2 and a half screenfuls of text to explain it. I defy anyone to reimplement the code from scratch in their heads, even after having it explained to them.
cHao
@cHao I managed the piece before the edit after a few beers (it took a couple of tries, I admit). The description is long, but the idea is simple, just make a counter and do work as the counters get incremented. I would be very surprised if the code was more complex than the the recursive version with an explicit stack (Writing code with an explicit stack is easier said than done). I suspect that one could get the counter version by writing the recursive version in continuation passing form and simplifying, but I doubt that the desriptipon would be simpler.
deinst
@deinst: using an explicit stack...yeah. Wouldn't be much better. I was going to do an iterative version, one much like yours in fact...then there was the whole wait-when-do-i-do-this-step? part. The recursive version came to me nearly instantly -- recursion is just way more natural for this kind of thing IMO, particularly when it's real recursion and not the hey-i'm-looping-and-pushing-and-popping-manually-so-it's-not-quite-recursion! method.
cHao
@deinst: This solution IS using an explicit stack - but you're giving it a fixed size at the beginning and only storing one variable at each level. This is just more complicated than it needs to be.
Eclipse
@Eclipse Yes, but the returns do not need to be stored here. You are going to need to store the variables anyway. I challenge you, or anyone else, to write out the recursive version with an explicit stack that uses no more than the counter variables themselves and a fixed amount of space without passing through CPS form or something like that.
deinst
@Eclipse (continued) I have nothing against recursion, but converting from a recursive version to one with an explicit stack is often easier said than done. In this case, I am quite convinced that it is easier to get the answer from first principles than it is from transformation of a recursive program. I think that an answer that says 'write it recursively, then transform it to have an explicit stack' without actually performing said transformation is unhelpful in the extreme.
deinst
@deinst: check my other answer. I got bored.
cHao
+7  A: 

Write your algorithm exactly as you would using recursion, but use an explicit stack object instead of recursion. I.e.:

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.
}
Billy ONeal
Like I said down in the questions you down voted, the recursion solution is going to better for this due to the nature of the question . Why don't you want to use recursion?
Nathan W
Some people are scared of recursion. It bit them in the backside once before, or their teachers couldn't explain how to do it correctly, or both.
cHao
@roygbiv What you are doing in your original code snippet is isomorphic to using a stack. As is recursion. You're not going to find an iterative solution that _doesn't_ use a stack. I stand with @Nathan: Why _not_ use recursion?
Nick Johnson
@roygbiv: What do you mean by "impossibly generic"? Your question is a generic question. I agree that it's not elegant -- the most elegant solution is recursion, which you explicitly disallowed. The explicit stack is the next best solution.
Billy ONeal
@roygbiv: For no logical reason that you've cared to share, you're categorically dismissing the concept that's *made* for tasks like this. Til you can explain that, yeah -- you're scared of it.
cHao
For that matter, "iterative, dynamic solution" sounds like a great way to describe using an explicit stack.
Domingo Galdos
@roygbiv: Hmm.. I guess I don't understand the meaning of the word "dynamic" then. If you could please explain what you mean by that, it would be nice.
Billy ONeal
@roygbiv: He can't correct it now -- it's been too long. Deal with it. As for changing the number of for loops, you cannot do that unless you either use a stack, or are using a language which supports something like `eval`. .NET languages can't do that without a LOT of effort. I simply don't understand why you can't use a stack or recursion -- these things are designed to solve EXACTLY the kind of problem you have.
Billy ONeal
@roygbiv: As for bad attitude, your comment indicates that you *also* have a bad attitude, and refuse to respond to @cHao's pefectly valid point.
Billy ONeal
@roygbiv: This solution is not recursive.
Billy ONeal
@roygbiv: Not asking for a recursive solution is not the same thing as outright rejecting a valid solution because you don't like recursion. Most people, when presented with the solutions on this page and asked to use one, would choose the recursive solutions -- they're more natural, less tricky, and above all *shorter*. That you refuse to do so is beyond illogical.
cHao
@roygbiv: Yes, and if you look at my words, you'll note that they were stack **or** recursion. This solution uses a stack, not recursion.
Billy ONeal
@roygbiv: If you can't code the iterative solution off the top of your head, **it doesn't matter which is faster. The code that's easier to understand is the better code**, in 99% of all cases. And i guaranfreakingtee you aren't involved in the other 1%.
cHao
@roygbiv: Everybody thinks they're in the "other 1%". And 99% of them *aren't*. The 1% who actually *are*, it tends to be pretty obvious. And you've already said you have no real code, meaning you can't have profiled it -- which any 1%'er does before even *considering* optimization. So yeah, my point stands.
cHao
@roygbiv: The context of what you're doing is irrelevant to this -- the things i said aren't going to be any less correct if you explain yourself. My "facts" have yet to be refuted, so the quotes are hereby removed from the word until you can provide reason to put them back on. My "bad attitude" is nothing more than my flatly and repeatedly telling you you're wrong, as by all indications you are -- and til you're right, or at least stop trying to pretend you are, it will continue.
cHao
+6  A: 
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

Far more elegant, shorter, less tricky than any iterative solution you can come up with. Downvote me all ya want, doesn't make it less true.

cHao
Agreed. It'd be a lot more elegant if it weren't written in VB, though. ;)
Nick Johnson
Psh. The original code was in VB. Just trying to be helpful. :) Besides, i kinda like VB. Its biggest difference from C# these days is that it's wordier (which i like; `end if : end while : end sub` is easier to check for correctness than `}}}`). Oh, and it has properties that do what you expect when you pass them by reference.
cHao
@roygbiv: Pseudocode that compiles is no longer pseudocode -- it's *code*. Your code doesn't just resemble VB -- it *compiles* as VB. Reasonable to expect that that's what it is, considering the keywords used are quite a bit less common outside of BASIC.
cHao
+2  A: 

I would upvote deinst's solution, but I'm new and don't have the 15 rep... After tracing each algorithm, I do believe deinst has the correct dynamic-iterative approach. However, deinst initializes all at once at the start, while roygbiv initializes repeatedly within the loop nest. (I think I had seen a correct prior edit of deinst's.)

William
I believe that it is correct now. The initialization is done at each step. The loop looks backwards, but that is because I wrote it after a couple of beers. It would be better as a do while loop. I am certain that the counter algorithm is what is wanted.
deinst
You should have your 15 rep now. :)
cHao
A: 

You can construct a collection of sequences, where each sequence is a permutation of your loop variables i,j,k,...

Once you have this collection, you can then iterate:

foreach (var seq in myCollection)
{
  initialize(seq[0]);
  initialize(seq[1]);
  initialize(seq[2]);
  ...
  doSomething(...);
}

One way to generate the collection of sequences, courtesy of Eric Lippert, via 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}));                
}

You pass a sequence {{1,2},{1,2},...{1,2}} of any length, where each inner {1,2} sequence replaces one of your loop variables, and you get back a sequence of all permutations of the values.

mbeckish
Any reason for the downvote?
mbeckish
+1  A: 

You will need to keep track of depth variables where depth is the total depth of your nesting. You can use an array or stack for that, or you can use the power of recursion and get away with allocating a single local variable at a time

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));
  }
}

Since i,j,k,... range from 1 to 2, it might also be possible to interpret them as bits of a single integer variable.

Note that your nested loops perform 2^depth operations in total.

Heinrich Apfelmus
This would initialize the innermost item with each iteration, which is different from the code given. The initialize() needs to be outside the loop. Yeah, it doesn't make sense -- `i` isn't used yet, so using it outside the loop is kinda hinky -- but that's what you'd need in order to do the same thing as the original. That and some memory of the latest values of the inner counters.
cHao
I took the liberty to use the "correct" order for initialization. ;-) The OP's code uses only one counter at a time for `doSomething`, so there is no need to store them explicitly. The outer counters (`m`, `l`, ..) are implicit in the call stack, because `var i` is a fresh variable on each call.
Heinrich Apfelmus
In the original code that was in the question, variables were being used outside of their respective loops. The variables at that point would be either 0 (before the first time through the respective loop) or 3 (after). Duplicating that functionality would require remembering the counters' values. Your code doesn't need to, though, since the variables aren't used til they have a known value.
cHao
+1  A: 
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];
    }
}

Still way trickier than recursion. Don't use this unless you absolutely have to.

cHao
Very nice. It certainly is not obvious to me.
deinst