views:

711

answers:

3

I have been playing with this a while, and just cannot see an obvious solution. I want to remove the recursion from the XinY_Go function.

def XinY_Go(x,y,index,slots):
   if (y - index) == 1:
      slots[index] = x
      print slots
      slots[index] = 0
      return
   for i in range(x+1):
      slots[index] = x-i
      XinY_Go(x-(x-i), y, index + 1, slots)

def XinY(x,y):
   return XinY_Go(x,y,0,[0] * y)

The function is calculating the number of ways to put X marbles in Y slots. Here is some sample output:

 >>> xy.XinY(1,2)
 [1, 0]
 [0, 1]
 >>> xy.XinY(2,3)
 [2, 0, 0]
 [1, 1, 0]
 [1, 0, 1]
 [0, 2, 0]
 [0, 1, 1]
 [0, 0, 2]
+20  A: 

Everything we think of as recursion can also be thought of as a stack-based problem, where the recursive function just uses the program's call stack rather than creating a separate stack. That means any recursive function can be re-written using a stack instead.

I don't know python well enough to give you an implementation, but that should point you in the right direction. But in a nutshell, push the initial arguments for the function onto the stack and add a loop that runs as long as the size of the stack is greater than zero. Pop once per loop iteration, push every time the function currently calls itself.

Joel Coehoorn
True. And I realize this, I was having trouble figuring out what to put on the stack and when. I guess it won't hurt for me to think on the problem a bit more. :)
grieve
I did a fortran77 Tower of Hanoi once just to prove the point...
dmckee
A long time ago I removed recursion from Towers of Hanoi, by noticing a pattern in the sum of two of each of the towers at any given point. With that based on the number of starting disks (odd or even) you could always know which disk to move next. Now if I could just find my notes on that.
grieve
+2  A: 

Look at this code for creating all permutations, I guess I'd be relatively simple to implement something similar for your problem.

How to generate all permutations of a list in python?

Georg
+10  A: 

A naive implementation of @Joel Coehoorn's suggestion follows:

def XinY_Stack(x, y):
    stack = [(x, 0, [0]*y)]
    while stack:
        x, index, slots = stack.pop()
        if (y - index) == 1:
            slots[index] = x
            print slots
            slots[index] = 0
        else:
            for i in range(x + 1):
                slots[index] = x-i
                stack.append((i, index + 1, slots[:]))

Example:

>>> XinY_Stack(2, 3)
[0, 0, 2]
[0, 1, 1]
[0, 2, 0]
[1, 0, 1]
[1, 1, 0]
[2, 0, 0]

Based on itertools.product

def XinY_Product(nmarbles, nslots):
    return (slots
            for slots in product(xrange(nmarbles + 1), repeat=nslots)
            if sum(slots) == nmarbles)

Based on nested loops

def XinY_Iter(nmarbles, nslots):
    assert 0 < nslots < 22 # 22 -> too many statically nested blocks
    if nslots == 1: return iter([nmarbles])
    # generate code for iter solution
    TAB = "  "
    loopvars   = []
    stmt       = ["def f(n):\n"]
    for i in range(nslots - 1):
        var = "m%d" % i
        stmt += [TAB * (i + 1), "for %s in xrange(n - (%s)):\n"
                 % (var, '+'.join(loopvars) or 0)]
        loopvars.append(var)

    stmt += [TAB * (i + 2), "yield ", ','.join(loopvars),
             ', n - 1 - (', '+'.join(loopvars), ')\n']
    print ''.join(stmt)
    # exec the code within empty namespace
    ns = {}
    exec(''.join(stmt), ns, ns)
    return ns['f'](nmarbles + 1)

Example:

>>> list(XinY_Product(2, 3))
[(0, 0, 2), (0, 1, 1), (0, 2, 0), (1, 0, 1), (1, 1, 0), (2, 0, 0)]
>>> list(XinY_Iter(2, 3))
def f(n):
  for m0 in xrange(n - (0)):
    for m1 in xrange(n - (m0)):
      yield m0,m1, n - 1 - (m0+m1)

[(0, 0, 2), (0, 1, 1), (0, 2, 0), (1, 0, 1), (1, 1, 0), (2, 0, 0)]
J.F. Sebastian
Thanks! It seems kind of obvious now. :)
grieve