Function with Varying Number of for Loops (Python)

Function with varying number of For Loops (python)

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()

Function with varying number of for loops using the arguments in the loop

You can use recursion to implement this, but the simplest would be just to use itertools.product:

from itertools import product

def f(*sets):
for p in product(*sets):
# e.g. (1, 3, 5)
print(*p)

>>> f([1, 2], [3, 4], [5, 6])
1 3 5
1 3 6
1 4 5
1 4 6
2 3 5
2 3 6
2 4 5
2 4 6

product returns the cartesian product of the input iterables in the form of a lazy iterator over tuples.

A simple recursive implementation of the cartesian product would go along the following lines:

def f(*sets):
if not sets:
return [[]]
result = []
for head in sets[0]:
for combo in f(*sets[1:]):
result.append([head] + combo)
return result

>>> f([1, 2], [3, 4])
[[1, 3], [1, 4], [2, 3], [2, 4]]

How to create a function with a variable number of 'for' loops, each with a distinct index?

One easy way is using itertools cartesian product:

from itertools import product 
L, D = 2, 2
print(list(product(list(range(L)), repeat = D)))

Result

[(0, 0), (0, 1), (1, 0), (1, 1)]

How to create variable dual for loops?

Using itertools.product with its 'repeat' argument.

import itertools as it

def function(n, max_anz, max_size, step):
lst = [list(e) for e in it.product(range(max_anz+1), range(0, max_size, step), repeat = n)]
print(*lst, sep='\n')
print(len(lst))

Or with a generator to prevent building the full list:

def function(n, max_anz, max_size, step):
g = it.product(range(max_anz+1), range(0, max_size, step), repeat = n)

i = 0
for e in g:
i += 1
print(list(e))
print(i)

which could be shortened:

def function(n, max_anz, max_size, step):
i = 0
for e it.product(range(max_anz+1), range(0, max_size, step), repeat = n):
i += 1
print(list(e))
print(i)

Variable number of loops in function (python)

you can pass your variable argument list to itertools.product (with a generator comprenehsion to convert the dicts to their items), then print the flattened results (since product returns tuples of tuples):

from __future__ import print_function
import itertools

d1 = {'a':1,'b':2,'c':3}
d2 = {'aa':11,'bb':22,'cc':33}
d3 = {'aaa':111,'bbb':222,'ccc':333}

def nLoop(*args):
for t in itertools.product(*(a.items() for a in args)):
print(*(x for a in t for x in a))

nLoop(d1,d2,d3)

The output of this new nLoop function is identical to yours (if order isn't considered, since dictionary order may change between runs)

Note that this is a Python 3 compliant solution, but also works with Python 2.

Variable number of nested for loops with fixed range

It's important for one to develop the skills to reason about these problems. In this case, Python includes itertools.product but what happens the next time you need to write a behaviour specific to your program? Will there be another magical built-in function? Maybe someone else will have published a 3rd party library to solve your problem?

Below, we design product as a simple recursive function that accepts 1 or more lists.

def product (first, *rest):
if not rest:
for x in first:
yield (x,)
else:
for p in product (*rest):
for x in first:
yield (x, *p)

for p in product (range(2), range(2), range(2)):
print ('x: %d, y: %d z: %d' % p)

# x: 0, y: 0 z: 0
# x: 1, y: 0 z: 0
# x: 0, y: 1 z: 0
# x: 1, y: 1 z: 0
# x: 0, y: 0 z: 1
# x: 1, y: 0 z: 1
# x: 0, y: 1 z: 1
# x: 1, y: 1 z: 1

Assuming you want a more conventional iteration ordering, you can accomplish do so by using an auxiliary loop helper

def product (first, *rest):
def loop (acc, first, *rest):
if not rest:
for x in first:
yield (*acc, x)
else:
for x in first:
yield from loop ((*acc, x), *rest)
return loop ((), first, *rest)

for p in product (range(2), range(2), range(2)):
print ('x: %d, y: %d z: %d' % p)

# x: 0, y: 0 z: 0
# x: 0, y: 0 z: 1
# x: 0, y: 1 z: 0
# x: 0, y: 1 z: 1
# x: 1, y: 0 z: 0
# x: 1, y: 0 z: 1
# x: 1, y: 1 z: 0
# x: 1, y: 1 z: 1

Varying number of related nested for loops

The comment about the complexity of the solutions is correct. It gets big quickly. Having said that, to address your original question, you can do this with a fairly simple recursive function for small input. Basically you start with an array of dice, pop one off add it to a sum and recurse with that sum and the rest of the array.

For example:

function sums(dice, sum = 0, ans = []) {  if (dice.length === 0) ans.push(sum) // edge case, no more dice  else {    let d = dice[0]    for (let i = 1; i <= d; i++) {      sums(dice.slice(1), sum + i, ans) // recurse with remaining dice    }    return ans  }}
// two six-sided dicelet ans = sums([6, 6]) console.log(JSON.stringify(ans.sort((a, b) => a - b)))
// three three-sided diceans = sums([3, 3, 3]) console.log(JSON.stringify(ans.sort((a, b) => a - b)))


Related Topics



Leave a reply



Submit