Function with varying number of For Loops (python) [duplicate]
Asked Answered
L

8

50

My problem is difficult to explain.

I want to create a function that contains nested for loops,
the amount of which is proportional to an argument passed to the function.

Here's a hypothetical example:

Function(2)

...would involve...

for x in range (y):
    for x in range (y):
        do_whatever()

Another example...

  Function(6)

...would involve...

for x in range (y):
    for x in range (y):
        for x in range (y):
            for x in range (y):
                for x in range (y):
                    for x in range (y):
                        whatever()

The variables of the for loop (y) are NOT actually used in the nested code.

Your first thought might be to create ONE for loop, with a range that is to the power of the number argument...
THIS CAN NOT WORK because the product would be HUGE. I have instances required where there are 8 nested for loops.
The product is too large for a range in a for loop.

There are other arguments needed to be passed to the function, but I can handle that myself.

Here's the code (it creates the Snowflake Fractal)

from turtle import *
length = 800
speed(0)

def Mini(length):
    for x in range (3):
        forward(length)
        right(60)

penup()
setpos(-500, 0)
pendown()   

choice = input("Enter Complexity:")

if choice == 1:
    for x in range (3):
        forward(length)
        left(120)

elif choice == 2:
    for x in range (3):
        Mini(length/3)
        left(120)

if choice == 3:
    for x in range (6):
        Mini(length/9)
        right(60)
        Mini(length/9)
        left(120)

if choice == 4:
    for y in range (6):
        for x in range (2):
            Mini(length/27)
            right(60)
            Mini(length/27)
            left(120)
        right(180)
        for x in range (2):
            Mini(length/27)
            right(60)
            Mini(length/27)
            left(120)

if choice == 5:
    for a in range (6):
        for z in range (2):
            for y in range (2):
                for x in range (2):
                    Mini(length/81)
                    right(60)
                    Mini(length/81)
                    left(120)
                right(180)
                for x in range (2):
                    Mini(length/81)
                    right(60)
                    Mini(length/81)
                    left(120)
            right(180)
        right(180)

if choice == 6:
    for c in range (6):
        for b in range (2):
            for a in range (2):
                for z in range (2):
                    for y in range (2):
                        for x in range (2):
                            Mini(length/243)
                            right(60)
                            Mini(length/243)
                            left(120)
                        right(180)
                        for x in range (2):
                            Mini(length/243)
                            right(60)
                            Mini(length/243)
                            left(120)
                    right(180)
                right(180)
            right(180)
        right(180)

if choice == 7:
    for a in range (6):
        for b in range(2):
            for c in range (2):
                for d in range (2):
                    for e in range (2):
                        for f in range (2):
                            for y in range (2):
                                for x in range (2):
                                    Mini(length/729)
                                    right(60)
                                    Mini(length/729)
                                    left(120)
                                right(180)
                                for x in range (2):
                                    Mini(length/729)
                                    right(60)
                                    Mini(length/729)
                                    left(120)
                            right(180)
                        right(180)
                    right(180)
                right(180)
            right(180)
        right(180)

I'd appreciate any help you can give me at all,
though if you suggest a different method (such as recursion),
please don't just paste the code; instead, suggests some ideas that could put me in the right direction.

(The algorithm is for a Specialist Math Assignment)


specs:
Python 2.7.1
Turtle
IDLE
Windows7

Laurettelauri answered 25/8, 2011 at 7:23 Comment(6)
Is there anything about using recursion for this that you don't understand?Waggle
If you're concerned about the size of range then just use xrange.Dimissory
@Dimissory NameError: name 'xrange' is not definedJovian
@Jovian in python3, range has the same behaviour as python2's xrangeLaurettelauri
"THIS CAN NOT WORK because the product would be HUGE. I have instances required where there are 8 nested for loops." Well, you know, 2**8 is only 256. More importantly, the nested loops would have to do the same amount of iteration.Eleazar
The question that is claimed to already contain an answer to this one is just a very different question...Wimbush
E
31

This problem can be solved by recursion. I am just writing an algorithm here, since I believe this can be a general problem.

function Recurse (y, number) 
   if (number > 1)
      Recurse ( y, number - 1 )
   else
      for x in range (y)
          whatever()
Engrossing answered 25/8, 2011 at 7:32 Comment(3)
Recursion is certainly one way of tackling this problem but the above algorithm is wrong; regardless of the value of number, it only loops y times. See the solution provided by @RobertMartin for the correct code/algorithm.Psychiatrist
@Psychiatrist hey RobertMartins answer was wrongJovian
@Ozair how can I do when i do different things on each loop ? for example i was printing the sum of i,j,k (index of for loop) or iterating through list and printing ?Jovian
B
51

I'm not clear why you can't use the product of the bounds and do

for x in range(y exp n)

where n is the # of loops.... You say y exp n will be huge, but I'm sure python can handle it.

However, that being said, what about some sort of recursive algorithm?

def loop_rec(y, n):
    if n >= 1:
        for x in range(y):
            loop_rec(y, n - 1)
    else:
       whatever()
Baculiform answered 25/8, 2011 at 7:28 Comment(0)
E
31

This problem can be solved by recursion. I am just writing an algorithm here, since I believe this can be a general problem.

function Recurse (y, number) 
   if (number > 1)
      Recurse ( y, number - 1 )
   else
      for x in range (y)
          whatever()
Engrossing answered 25/8, 2011 at 7:32 Comment(3)
Recursion is certainly one way of tackling this problem but the above algorithm is wrong; regardless of the value of number, it only loops y times. See the solution provided by @RobertMartin for the correct code/algorithm.Psychiatrist
@Psychiatrist hey RobertMartins answer was wrongJovian
@Ozair how can I do when i do different things on each loop ? for example i was printing the sum of i,j,k (index of for loop) or iterating through list and printing ?Jovian
W
8

Recursion will be your best bet. Consider what it should do in the base case and in the recursive case.

Code left out, as per request.

Waggle answered 25/8, 2011 at 7:27 Comment(0)
A
6

Here you go. Let ranges be your ranges, operate on result when you need to.

ranges=((1,4),(0,3),(3,6))
from operator import mul
operations=reduce(mul,(p[1]-p[0] for p in ranges))-1
result=[i[0] for i in ranges]
pos=len(ranges)-1
increments=0
print result
while increments < operations:
    if result[pos]==ranges[pos][1]-1:
        result[pos]=ranges[pos][0]
        pos-=1
    else:
        result[pos]+=1
        increments+=1
        pos=len(ranges)-1 #increment the innermost loop
        print result

[1, 0, 3]
[1, 0, 4]
[1, 0, 5]
[1, 1, 3]
[1, 1, 4]
[1, 1, 5]
[1, 2, 3]
[1, 2, 4]
[1, 2, 5]
[2, 0, 3]
[2, 0, 4]
[2, 0, 5]
[2, 1, 3]
[2, 1, 4]
[2, 1, 5]
[2, 2, 3]
[2, 2, 4]
[2, 2, 5]
[3, 0, 3]
[3, 0, 4]
[3, 0, 5]
[3, 1, 3]
[3, 1, 4]
[3, 1, 5]
[3, 2, 3]
[3, 2, 4]
[3, 2, 5]
[1, 0, 4]

Testing with the following would give the same result:

for x in range(*ranges[0]):
    for y in range(*ranges[1]):
        for z in range(*ranges[2]):
            print [x,y,z]
Apo answered 25/8, 2011 at 8:52 Comment(0)
B
4

A nice implementation of the varying number of For Loops problem:

def for_recursive(number_of_loops, range_list, execute_function, current_index=0, iter_list = []):

if iter_list == []:
    iter_list = [0]*number_of_loops

if current_index == number_of_loops-1:
    for iter_list[current_index] in range_list[current_index]:
        execute_function(iter_list)
else:
    for iter_list[current_index] in range_list[current_index]:
        for_recursive(number_of_loops, iter_list = iter_list, range_list = range_list,  current_index = current_index+1, execute_function = execute_function) 

An example of how to use it:

def do_whatever(index_list):
    return print(index_list)


for_recursive(range_list = [range(0,3), range(0,3) , range(1,3)], execute_function = do_whatever , number_of_loops=3)

The code returns the same that this code:

for i in range(0,3):
    for j in range(0,3):
        for k in range(1,3):
            print([i,j,k])
Bergson answered 24/10, 2019 at 11:22 Comment(0)
P
2

Have you considered xrange ?

for x in xrange(y ** n):
    whatever()

And if you overshoot even xrange limit, you can use itertool

import itertools
for x in itertools.product(xrange(y), repeat=n):
   whatever()

(previous itertool answer incorrectly used n for the range instead of y)

Pochard answered 25/8, 2011 at 10:12 Comment(1)
...but actually, your actual problem is not the first one you describe, as you want an extra "right(180)" in each loop. So go recursive is the right answerPochard
G
1

Here is another option for iterative solution, seems simpler to me. The idea is to use an analogy with a 10 or X-based numbering system. Where you basically increase your number/count by one each time but the representation changes according to the base. I.e. if the base is 10, then the number changes 1 .. 9 10 11 .. 19 20... Imagine that we want to loop on i,j,k from 0 .. 9 for each. We run the loop for counter in range(101010) and take the digits as the values of the number. E.g. 731 means i=7,j=3, k=1. To make the case more general, where the range for each i/j/... is different - we take modulo that range:

`

ranges = [2,3,4]
lenr = len(ranges)
for i in range(2*3*4):
    perm = []
    d, r  = i, 0
    for rng_i in (1, lenr):
        d, r = divmod(d, ranges[lenr - rng_i])
        perm.append(r)
    perm.extend([0]*(lenr-len(perm)))   # pad with zeros
    print (list(reversed(perm)))        # need to reverse as appended from right

Output will be:

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

`

Galore answered 7/7, 2020 at 15:46 Comment(0)
H
0

My reply is late, but supposing that you want to do multiple loops, e.g. print some range multiple times. Then the correct version of this recursion is:

def loop_rec(y, number):
   if (number > 1):
      loop_rec( y, number - 1 )
      for i in range(y): 
         print(i, end=' ')        
   else:      
      for i in range(y):
         print(i, end=' ')

loop_rec(4,3)

This will create three for loops with the range(4)

If you want to play around with dynamic range, here are some variants:

def loop_rec(y, number):
if (number > 1):
    loop_rec( y+1, number - 1 )
    for i in range(y): 
        print(i, end=' ')
    print(' ;')
else:      
    for i in range(y):
        print(i, end=' ')
    print(';')

loop_rec(6,4)

which will print out:

0 1 2 3 4 5 6 7 8 ;
0 1 2 3 4 5 6 7  ;
0 1 2 3 4 5 6  ;
0 1 2 3 4 5  ;

or

def loop_rec(y, number):
if (number > 1):
    loop_rec( y-1, number - 1 )
    for i in range(y): 
        print(i, end=' ')
    print(' ;')
else:      
    for i in range(y):
        print(i, end=' ')
    print(';')
loop_rec(6,4)

which will output:

0 1 2 ;
0 1 2 3  ;
0 1 2 3 4  ;
0 1 2 3 4 5  ;

A better variant which is using only one for loop (less typing) is the following:

def loop_rec(y, number):
    if (number >= 1):
        loop_rec( y+1, number - 1 )
        for i in range(y): 
            print(i, end=' ')
        print('')
    else:      
        return

loop_rec(1,5)

will output:

0 1 2 3 4 
0 1 2 3 
0 1 2 
0 1 
0 
Hopeless answered 3/10, 2016 at 18:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.