Iterating n python iterators at once - python

I'm trying to iterate a bunch of data ranges at once, to get the combinations of all their values.
The number of ranges can differ, but I have them collected in a list.
Is there a way to iterate them using list comprehension or a similar clean, pythonic way?
This is what I mean by iterating together:
[print(i, j) for i in r1 for j in r2]
So that's a simple example with two known ranges, but what I need is more like
[print(i, j, ...) for i in r1 for j in r2 for k in r3...]
Note: i don't just need a list of number combinations, the iterators are my own iterator class which works similarly to range() but allows me to also get current state without calling next(), which would alter the state.
My iterator class sets its value back to the start on StopIteration, so it can be looped through more than once.
Here you can see the class:
#dataclass
class Range:
start: float
end: float
step: float = field(default=1)
includeEnd: bool = field(default=True)
def __post_init__(self):
self.value = self.start
def __next__(self):
v = self.value
end = v > self.end if self.includeEnd else v >= self.end
if not end:
self.value += self.step
return v
else:
self.value = self.start
raise StopIteration
def __iter__(self):
return self

But how would you get the product of n iterators using itertools.product(), when you have a list of n iterators?
itertools.product(*the_list). Nothing special about product() there. The leading * is general Python syntax for treating a list (more generally, an iterable) as a sequence of individual arguments.
>>> from itertools import product
>>> args = [range(2), range(3), (i**2 for i in [5, 9])]
>>> args
[range(0, 2), range(0, 3), <generator object <genexpr> at 0x000001E2E7A710B0>]
>>> for x in product(*args):
... print(x)
(0, 0, 25)
(0, 0, 81)
(0, 1, 25)
(0, 1, 81)
(0, 2, 25)
(0, 2, 81)
(1, 0, 25)
(1, 0, 81)
(1, 1, 25)
(1, 1, 81)
(1, 2, 25)
(1, 2, 81)

Related

Compute all ways to bin a series of integers into N bins, where each bin only contains contiguous numbers

I want find all possible ways to map a series of (contiguous) integers M = {0,1,2,...,m} to another series of integers N = {0,1,2,...,n} where m > n, subject to the constraint that only contiguous integers in M map to the same integer in N.
The following piece of python code comes close (start corresponds to the first element in M, stop-1 corresponds to the last element in M, and nbins corresponds to |N|):
import itertools
def find_bins(start, stop, nbins):
if (nbins > 1):
return list(list(itertools.product([range(start, ii)], find_bins(ii, stop, nbins-1))) for ii in range(start+1, stop-nbins+2))
else:
return [range(start, stop)]
E.g
In [20]: find_bins(start=0, stop=5, nbins=3)
Out[20]:
[[([0], [([1], [2, 3, 4])]),
([0], [([1, 2], [3, 4])]),
([0], [([1, 2, 3], [4])])],
[([0, 1], [([2], [3, 4])]),
([0, 1], [([2, 3], [4])])],
[([0, 1, 2], [([3], [4])])]]
However, as you can see the output is nested, and for the life of me, I cant find a way to properly amend the code without breaking it.
The desired output would look like this:
In [20]: find_bins(start=0, stop=5, nbins=3)
Out[20]:
[[(0), (1), (2, 3, 4)],
[(0), (1, 2), (3, 4)],
[(0), (1, 2, 3), (4)],
[(0, 1), (2), (3, 4)],
[(0, 1), (2, 3), (4)],
[(0, 1, 2), (3), (4)]]
I suggest a different approach: a partitioning into n non-empty bins is uniquely determined by the n-1 distinct indices marking the boundaries between the bins, where the first marker is after the first element, and the final marker before the last element. itertools.combinations() can be used directly to generate all such index tuples, and then it's just a matter of using them as slice indices. Like so:
def find_nbins(start, stop, nbins):
from itertools import combinations
base = range(start, stop)
nbase = len(base)
for ixs in combinations(range(1, stop - start), nbins - 1):
yield [tuple(base[lo: hi])
for lo, hi in zip((0,) + ixs, ixs + (nbase,))]
Then, e.g.,
for x in find_nbins(0, 5, 3):
print(x)
displays:
[(0,), (1,), (2, 3, 4)]
[(0,), (1, 2), (3, 4)]
[(0,), (1, 2, 3), (4,)]
[(0, 1), (2,), (3, 4)]
[(0, 1), (2, 3), (4,)]
[(0, 1, 2), (3,), (4,)]
EDIT: Making it into 2 problems
Just noting that there's a more general underlying problem here: generating the ways to break an arbitrary sequence into n non-empty bins. Then the specific question here is applying that to the sequence range(start, stop). I believe viewing it that way makes the code easier to understand, so here it is:
def gbins(seq, nbins):
from itertools import combinations
base = tuple(seq)
nbase = len(base)
for ixs in combinations(range(1, nbase), nbins - 1):
yield [base[lo: hi]
for lo, hi in zip((0,) + ixs, ixs + (nbase,))]
def find_nbins(start, stop, nbins):
return gbins(range(start, stop), nbins)
This does what I want; I will gladly accept simpler, more elegant solutions:
def _split(start, stop, nbins):
if (nbins > 1):
out = []
for ii in range(start+1, stop-nbins+2):
iterator = itertools.product([range(start, ii)], _split(ii, stop, nbins-1))
for item in iterator:
out.append(item)
return out
else:
return [range(start, stop)]
def _unpack(nested):
unpacked = []
if isinstance(nested, (list, tuple)):
for item in nested:
if isinstance(item, tuple):
for subitem in item:
unpacked.extend(_unpack(subitem))
elif isinstance(item, list):
unpacked.append([_unpack(subitem) for subitem in item])
elif isinstance(item, int):
unpacked.append([item])
return unpacked
else: # integer
return nested
def find_nbins(start, stop, nbins):
nested = _split(start, stop, nbins)
unpacked = [_unpack(item) for item in nested]
return unpacked

Every way to organize N objects in M list slots

I'm trying to find a way, using built-in functions, to list every way to organize N balls in M slots. The balls can stack in the slots. For example:
N = 2, M = 3 -> {|0|1|1|, |1|0|1|, |1|1|0|, |2|0|0|, |0|2|0|, |0|0|2|}
itertools.permutations() is part of the puzzle, but how can you go through all possible stacks of balls that preserves N?
Let oo represent two balls.
The problem of placing 2 balls in 3 slots is equivalent to the problem of placing 2 balls around 2 sticks:
|o|o --> 0,1,1
o||o 1,0,1
o|o| 1,1,0
oo|| 2,0,0
|oo| 0,2,0
||oo 0,0,2
The sticks, | represent the edges of the slots. The number of balls in each slot is shown on the right.
Notice that in each row there are 4 locations, and 2 sticks. There is always one fewer stick than there are slots. So the problem is equivalent to finding all the ways to select 2 locations for the balls out of 4 possibilities. 4 choose 2.
In [98]: import itertools as IT
In [99]: list(IT.combinations(range(4), 2))
Out[99]: [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
These, then, are the possible locations for the balls.
All that's left to do is to compute into which slot these balls belong. Let's take (1, 3) as an example. The pictoral diagram for (1, 3) looks like this:
|o|o
It turns out that if you subtract (0, 1) elementwise from (1, 3), you get the slot for each ball:
(1, 3)
(0, 1)
------
(1, 2)
Thus, the first ball is in slot 1, the second in slot 2.
In general, if you subtract range(m-1) from the combination, you get the slot values. This makes some sense if you think of the amount you are subtracting as the number of balls that precede the current ball in the pictoral diagram. Since our diagrams consist of balls and slots, if you subtract the balls, what remains are slots.
import itertools as IT
def pigeonhole(n, m):
"""
Generate the ways n balls can placed in m slots
"""
for choice in IT.combinations(range(n+m-1), n):
slot = [c-i for i,c in enumerate(choice)]
result = [0]*m
for i in slot:
result[i] += 1
yield result
print(list(pigeonhole(2,3)))
yields
[[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]]
To find all assignments of N balls to M slots:
if N is 0 then
leave all M slots empty
otherwise, if M is 1, then
put all N balls to the only slot
otherwise
For each i in 0 .. N
put i balls in M-th slot, and
find all assignments of remaining N-i balls to remaining M-1 slots
Oh, here's a fun way to come at it:
>>> import random
>>> def permutate(N, M, lim = 1e6):
... for i in range(int(lim)):
... tup = ()
... for j in range(M):
... tup += (random.randrange(0,N+1),)
...
... if sum(tup) == N: yield tup
...
>>> permutes = []
>>> for p in permutate(2, 3):
... if not p in permutes:
... permutes.append(p)
...
>>> permutes
[(0, 1, 1), (0, 0, 2), (2, 0, 0), (0, 2, 0), (1, 0, 1), (1, 1, 0)]
>>>
First we create a generator that gives a valid tuple of M elements which sums to N. We do this in a Monte Carlo-esk approach by composing each element of of a random number from 0 to N+1. We do this M times, and append each guess to a tuple. We then check if the tuple is valid (sums to N). We then call this generator repeatedly until it is exhausted, and add non-repeat entries to a list.
Some Things to Note:
You'll need to tune lim to be appropriate for what you need. Generally it should be higher for larger M and N. You could modify this (with some effort) to estimate if lim is sized appropriate since you can figure how long permutes should be based off of N and M.
Monte Carlo methods are slow, which is why I categorized this as a "fun" approach
Consider using itertools.combinations_with_replacement.
You can easily do like this.
import itertools
def myfunc(n, m):
for j in itertools.combinations_with_replacement(xrange(m), n):
r = [0] * m
for i in j:
r[i] += 1
yield r
The problem is how to ensure this is correct.
I think #wnnmaw answer can be useful for that, because the logic is very simple.
#http://stackoverflow.com/a/22940260/2931409
import random
def permutate(N, M, lim = 1e6):
for i in range(int(lim)):
tup = []#<--Sorry changed here for test
for j in range(M):
tup += (random.randrange(0,N+1),)
if sum(tup) == N: yield tup
def perm(n, m):
permutes = []
for p in permutate(n, m):
if not p in permutes:
permutes.append(p)
return permutes
OK. Now check myfunc correctly works.
>>> N, M = 3, 5
>>> sorted(myfunc(N, M)) == sorted(perm(N, M))
True
Here's a nice way to do it.
NI = sum(in_pop)#number of input balls
# create the seed vector to be folded
u0 = [0] * NO
for i in range(NI):
u0[i] = 1
# create a storage basis matrix and fold the seed vector
basis = [u0]
ui = []
for i in arange(1,NI):
# copy the last vector
ui = basis[i - 1][:]
# find the number to 0 boundary
edge = ui.index(0)
# set the previous value to zero
tmp = ui[edge - 1]
ui[edge - 1] = 0
# increment the value behind that
ui[edge - 2] = ui[edge - 2] + tmp
print 'edge # ', edge
print 'ui', ui
basis.append(ui[:])
print basis
# run through the permutations in this basis
orderings = []
for ui in basis:
perms = set(itertools.permutations(ui))
orderings.extend(perms)
print orderings
The result is the following for input N=2, M=3:
edge # 2
ui [2, 0, 0]
[[1, 1, 0], [2, 0, 0]]
[(0, 1, 1), (1, 1, 0), (1, 0, 1), (0, 2, 0), (2, 0, 0), (0, 0, 2)]
[Finished in 0.5s]

Generate all permutations of n by n list with max value n-1 without recursion python

I want to generate all permutations of an n by n list with max value n-1, for example, for n=3, all the possible lists are as follows
[0,0,0]
[0,0,1]
[0,0,2]
[0,1,0]
[0,1,1]
[0,1,2]
[0,2,0]
...
[2,2,2]
I realize this grows very large very quickly (there are n^n permutations). I currently have the following working code which uses recursion
def generatePermutations(allPerms, curPerm, curIndex, n):
#allPerms is a reference to the full set of permutations
#curIndex is the index which is currently being changed
if (curIndex == n - 1):
for i in range(n):
curPerm[curIndex] = i
allPerms.append(list(curPerm))
else:
for i in range(n):
curPerm[curIndex] = i
#recursively generate all permutations past our curIndex
generatePermutations(allPerms, curPerm, curIndex + 1, n)
allPermutations = []
currentPermutation = []
n = 4
for i in range(n):
currentPermutation.append(0)
generatePermutations(allPermutations, currentPermutation, 0, n)
In trying to find a non-recursive solution I've hit a wall, I'm thinking there would have to be n number of nested for loops, which I can't figure out how to do for arbitrary n. The only ideas I've had are doing some kind of fancy adding of functions containing the loops to a list to be run somehow, or even more absurdly, generating the code programmatically and passing it to an eval call. My gut tells me these are more complicated than necessary. Can anyone think of a solution? thanks!
Simple way, with a library call:
import itertools
def lists(n):
return itertools.product(xrange(n), repeat=n)
This returns an iterator, rather than a list. You can get a list if you want by calling list on the result.
If you want to do this without foisting the job onto itertools, you can count in base n, incrementing the last digit and carrying whenever you hit n:
def lists(n):
l = [0]*n
while True:
yield l[:]
for i in reversed(xrange(n)):
if l[i] != n-1:
break
l[i] = 0
else:
# All digits were n-1; we're done
return
l[i] += 1
You can use itertools.permutations() to handle the whole problem for you, in one go:
from itertools import permutations
allPermutations = list(permutations(range(4))
The documentation includes Python code that details alternative implementations in Python for that function; a version using itertools.product(), for example:
from itertools import product
def permutations(iterable, r=None):
pool = tuple(iterable)
n = len(pool)
r = n if r is None else r
for indices in product(range(n), repeat=r):
if len(set(indices)) == r:
yield tuple(pool[i] for i in indices)
However, your expected output is just a product of range(3) over 3:
>>> from itertools import product
>>> for p in product(range(3), repeat=3):
... print p
...
(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 1, 0)
(0, 1, 1)
(0, 1, 2)
(0, 2, 0)
(0, 2, 1)
(0, 2, 2)
(1, 0, 0)
(1, 0, 1)
(1, 0, 2)
(1, 1, 0)
(1, 1, 1)
(1, 1, 2)
(1, 2, 0)
(1, 2, 1)
(1, 2, 2)
(2, 0, 0)
(2, 0, 1)
(2, 0, 2)
(2, 1, 0)
(2, 1, 1)
(2, 1, 2)
(2, 2, 0)
(2, 2, 1)
(2, 2, 2)
Permutations is a much shorter sequence:
>>> from itertools import permutations
>>> for p in permutations(range(3)):
... print p
...
(0, 1, 2)
(0, 2, 1)
(1, 0, 2)
(1, 2, 0)
(2, 0, 1)
(2, 1, 0)

Python: Recursion to count up an array of size n

I am struggling about a recursion problem, which should not be too hard, but for some reason I don't come up with a solution.
I have an array of size "n" and I want to count up each element from 0 to n in a way that I get each possible combination.
n = 3
[0,0,0]
[0,0,1]
[0,1,0]
[1,0,0]
[... ]
[3,3,3]
Can anyone help?
If you have to code it up yourself, and have to use recursion:
def gen(n, l, prefix=()):
if l == 0:
print prefix
else:
for i in range(n):
gen(n, l - 1, prefix + (i,))
gen(4, 3)
No need for (explicit) recursion:
import itertools
for comb in itertools.product(range(4), repeat=3):
print comb
produces:
(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 0, 3)
(0, 1, 0)
(0, 1, 1)
...
(3, 3, 2)
(3, 3, 3)
Here's one way to do it that makes the procedure very explicit:
def combinations(n, elements = None):
if elements == 0: return [[]]
if not elements: elements = n
result = []
for v in range(n + 1):
for subcombination in combinations(n, elements - 1):
result.append([v] + subcombination)
return result
There are more pythonic ways to do it that might have better performance, including comprehensions or generators, but it sounds like you're looking for an explicit implementation.

Tuple and recursive list conversion

A recursive list is represented by a chain of pairs. The first element of each pair is an element in the list, while the second is a pair that represents the rest of the list. The second element of the final pair is None, which indicates that the list has ended. We can construct this structure using a nested tuple literal. Example:
(1, (2, (3, (4, None))))
So far, I've created a method that converts a tuple of values or the value None into a corresponding rlist. The method is called to_rlist(items). Example:
>>> to_rlist((1, (0, 2), (), 3))
(1, ((0, (2, None)), (None, (3, None))))
How do I write the inverse of to_rlist, a function that takes an rlist as input and returns the corresponding tuple? The method should be called to_tuple(parameter). Example of what should happen:
>>> x = to_rlist((1, (0, 2), (), 3))
>>> to_tuple(x)
(1, (0, 2), (), 3)
Note: The method to_rlist works as intended.
This is what I have so far:
def to_tuple(L):
if not could_be_rlist(L):
return (L,)
x, y = L
if not x is None and not type(x) is tuple and y is None:
return (x,)
elif x is None and not y is None:
return ((),) + to_tuple(y)
elif not x is None and not y is None:
return to_tuple(x) + to_tuple(y)
Which gives me the following result (which is incorrect):
>>> x = to_rlist((1, (0, 2), (), 3))
>>> to_tuple(x)
(1, 0, 2, (), 3)
How can I fix my method to return a nested tuple properly?
def to_list(x):
if x == None:
return ()
if type(x) != tuple:
return x
a, b = x
return (to_list(a),) + to_list(b)
This one worked for my HW ;)
def to_rlist(items):
r = empty_rlist
for i in items[::-1]:
if is_tuple(i): r1 = to_rlist(i)
else: r1 = i
r = make_rlist(r1,r)
return r

Categories