1

I found great answer on parametric nested loops - Parametric nested loops in Python

However, my requirement is little bit more difficult

Problem:

This is 3-depth nested loop (I would like function that has n-depth):

for i in range(100 + 1):
    for j in range(i, 100 + 1):
        for k in range(j, 100 + 1):
            -> need to retrieve [i,j,k]

Note that each loop's start point is dynamic and changes with every parent loop

2
  • 1
    I'm not sure to understand, why not print(i, j, k)? Commented May 15, 2019 at 15:08
  • 1
    @olinox14 The question is about arbitrary deep nesting, the 3-depth code is an example. Commented May 15, 2019 at 15:25

4 Answers 4

2

Here's a recursive approach using a default argument. The numbered points below refer to the numbered comments in the code.

  1. (base) The depth is zero. We are done, so yield the combination, comb
  2. (inductive) The depth is at least 1. For each x in a range, delegate to the recursive generator using x as the new starting point for the nested range and append x to the combination, comb.
def nested_range (depth = 0, start = 0, end = 1, comb = ()):
  if depth == 0:
    yield comb                  #1
  else:
    for x in range(start, end): #2
      yield from nested_range(depth - 1, x, end, comb + (x,))

Here's a nested range three (3) levels deep -

for p in nested_range (3, 0, 4):
  print(p)

# (0, 0, 0)
# (0, 0, 1)
# (0, 0, 2)
# (0, 0, 3)
# (0, 1, 1)
# (0, 1, 2)
# (0, 1, 3)
# (0, 2, 2)
# (0, 2, 3)
# (0, 3, 3)
# (1, 1, 1)
# (1, 1, 2)
# (1, 1, 3)
# (1, 2, 2)
# (1, 2, 3)
# (1, 3, 3)
# (2, 2, 2)
# (2, 2, 3)
# (2, 3, 3)
# (3, 3, 3)

This implementation is a total function and provides a valid result when depth = 0 -

for p in nested_range (0, 0, 4):
  print(p)

# ()

And for good measure, here's the output of a nested range five (5) levels deep -

for p in nested_range (5, 0, 3):
  print(p)

# (0, 0, 0, 0, 0)
# (0, 0, 0, 0, 1)
# (0, 0, 0, 0, 2)
# (0, 0, 0, 1, 1)
# (0, 0, 0, 1, 2)
# (0, 0, 0, 2, 2)
# (0, 0, 1, 1, 1)
# (0, 0, 1, 1, 2)
# (0, 0, 1, 2, 2)
# (0, 0, 2, 2, 2)
# (0, 1, 1, 1, 1)
# (0, 1, 1, 1, 2)
# (0, 1, 1, 2, 2)
# (0, 1, 2, 2, 2)
# (0, 2, 2, 2, 2)
# (1, 1, 1, 1, 1)
# (1, 1, 1, 1, 2)
# (1, 1, 1, 2, 2)
# (1, 1, 2, 2, 2)
# (1, 2, 2, 2, 2)
# (2, 2, 2, 2, 2)
Sign up to request clarification or add additional context in comments.

Comments

2

This can be done recursively (as you guessed already) e.g. in this way:

def nest_gen(count, start=0, end=101):
    if count < 1:
        return
    elif count == 1:
        yield from ((r,) for r in range(start, end))
        return

    for i in range(start, end):
        yield from ((i,) + r for r in nest_gen(count - 1, i, end))


print(tuple(nest_gen(6, end=5)))

3 Comments

I like this solution more than my answer - it's exactly the same logic, but much more pythonic
Non-total function. Causes stack overflow when count = 0.
You're welcome but the codomain is still non-total. Please see the comment I made on Green Cloak's post for details.
2

This is the kind of thing that I would use recursion for - as I see you've noted in your tag. Something like this, for example:

def iterate(depth, i=0, maxrange=101):
    if depth <= 0:
        return (yield ())
    for j in range(i, maxrange):                    # for each value of j...
        if depth == 1:                              # base case:
            yield (j,)                              #    return a 1-tuple
        else:                                       # recursive case:
            for k in iterate(depth-1, j, maxrange): #    get a generator for the next level of recursion
                yield (j,) + k                      #    yield all (depth+1)-tuples from prepending j to the next layer of recursion

which, when called as iterate(3) should produce

[(0,0,0), (0,0,1), (0,0,2), ..., (0,0,100), (0,1,1), ..., (0,100,100), (1,1,1), ..., (99,99,99), (100,100,100)]

6 Comments

syntax error on your first line, missing :. program gives bad result when depth=0 is specified.
Thanks for pointing that out, I've done my best to address it
it's close but the codomain (possible outputs) is a bit off. You probably want return (yield ()) otherwise depth = 0 gives no result instead of an empty result. To understand why this is an issue, imagine if subtract(5,5) returns nothing (None) instead of 0, the "empty" number. The function depending on subtract's output would have to check if it received a value. By returning an "empty" value, the caller of subtract is guaranteed that a result will always be returned.
@user633183 Thanks, I've changed it accordingly. Would returning tuple() work as well?
return (yield tuple()) would be equivalent, yes.
|
2

Here is an iterative approach:

def iterate(max_range, dim):
    if dim == 0:  #handle edge case
        yield from iter(())
    elif dim == 1:
        yield [0]
    else:
        fields = [0]*dim
        while True:
            yield fields
            fields[-1] += 1
            for i in reversed(range(1, dim)):
                if fields[i] >= max_range:
                    fields[i - 1] += 1
                    fields[i] = min(fields[i - 1], max_range -1)
            if fields[0] == max_range:
                break

an example:

for i in iterate(4, 3):
    print(i)

gives:

[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 0, 3]
[0, 1, 1]
[0, 1, 2]
[0, 1, 3]
[0, 2, 2]
[0, 2, 3]
[0, 3, 3]
[1, 1, 3]
[1, 2, 2]
[1, 2, 3]
[1, 3, 3]
[2, 2, 3]
[2, 3, 3]
[3, 3, 3]

edit: added separate parameters for max_value and nesting level

3 Comments

I don't believe this fits the OP's requirements. The nesting level and the range max are two separate variables.
Nice update but one problem remains. If dim = 0 your program throws an error instead of returning a valid result. See the comments in GreenCloak's answer for more details.
If you consider dim = 0 as an valid input, then you are right. But it should not be to hard to check for dim == 0 and return an empty iterator or whatever is considered meaningful in this case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.