Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Name of Algorithm(s) for Looping over a Variable Number of Independent Lists

Please excuse the vagueness of my question, I don't have any formal training in CS. I'm pretty sure a solution already exists, but I can't find an answer because I don't know what question to ask.

Essentially, I'm looking for the name of an algorithm, or group of algorithms, to find all combinations of several lists where each list contains the possibilities for a single position. That is, some function that can perform the mapping:

((a,b,c), (1,2), (z,g,h), (7)) ->
    ((a,1,z,7), (a,1,g,7), (a,1,h,7), (a,2,z,7), ... (c,2,h,7))

such that the result can be used to iterate over all possible combinations of the per-position lists in order.

The number of lists is variable, and the size of each list is variable and independent of the other lists. All the example solutions are missing at least one of those criteria.

It would also be awesome if anyone knew of pseudocode or example implementations for any of those algorithms, or a Python package that can handle this.

I can and have solved the problem previously, but I would like to know if there are better solutions out there before I implement my design again.

Thanks for your time.

For those curious, my previous solution looked something like what is described in this paper, which is the only example solution I've found. However, it doesn't name the problem, nor does it give me a jumping off point for further research. Here is an (untested) Python listing of my general solution:

def nloop(*args):
  lists = args
  n = len(lists) # Number of lists

  # Exit if no lists were passed
  if n <= 0:
    raise StopIteration

  i = [0] * n # Current index of each list
  l = [len(L) for L in lists] # Length of each list

  # Exit if any list is zero-length
  if min(l) <= 0:
    raise StopIteration

  while True:
    # Create and yield a list using the current indices
    yield tuple( lists[x][ i[x] ] for x in range(n) )

    # Increment the indices for the next loop
    # Move to the next item in the last list
    i[-1] += 1
    # Check the lists in reverse order to carry any
    # indices that have wrapped
    for x in reverse(list(range(n))):
      if i[x] >= l[x]:
        i[x] = 0
        if x > 0:
          i[x-1] += 1

    # If the first list has wrapped, we're done
    if i[0] >= l[0]:
      break

  raise StopIteration
like image 418
Matt W Avatar asked Nov 18 '25 11:11

Matt W


1 Answers

You are looking for itertools.product. It is equivalent to the nested for loop of arbitrary depth that you describe.

For your particular example (with appropriate syntactic modifications):

>>> from itertools import product
>>> list(product(('a', 'b', 'c'), (1, 2), ('z', 'g', 'h'), (7,)))
[('a', 1, 'z', 7),
 ('a', 1, 'g', 7),
 ('a', 1, 'h', 7),
 ('a', 2, 'z', 7),
 ('a', 2, 'g', 7),
 ('a', 2, 'h', 7),
 ('b', 1, 'z', 7),
 ('b', 1, 'g', 7),
 ('b', 1, 'h', 7),
 ('b', 2, 'z', 7),
 ('b', 2, 'g', 7),
 ('b', 2, 'h', 7),
 ('c', 1, 'z', 7),
 ('c', 1, 'g', 7),
 ('c', 1, 'h', 7),
 ('c', 2, 'z', 7),
 ('c', 2, 'g', 7),
 ('c', 2, 'h', 7)]
like image 115
Mad Physicist Avatar answered Nov 21 '25 02:11

Mad Physicist



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!