Related
I have this code that creates permutations of a given number. It also give the permuatations based on the number of specified digits, so 2 would give the permutations of all possible 2 digit values. I have this looped so for a 4 digit number, it would loop giving all permutations scenarios, like 4,3,2 and 1 digit permutations scenarios. The problem im having is how to store the perm variable which stores the permutations. I tried making a multi array perm, then as the loop iterates it adds the new array to the perm. Didn't work because the arrays are different sizes. How can I continue?
def fp(number):
# A Python program to print all
# permutations using library function
from itertools import permutations
# Get all permutations of [1, 2, 3]
c= list(map(int,str(number)))
print(c, len(c))
i=1
while i <= len(c):
perm= permutations(c,i) #permuate the number c to the number of specified digits i
i+=1
# Print the obtained permutations
for i in list(perm):
print (i)
You are searching for a powerset, search in these functions by itertools for powerset. I just changed the combinations to permutations.
Then loop through all permutations and append them to a list (you could also use a dictionary)
import itertools
def powerset(iterable):
s = list(iterable)
return itertools.chain.from_iterable(itertools.permutations(s, r) for r in range(1,len(s)+1))
lst_of_numbers = [1, 2, 3]
out = []
for perm in powerset(lst_of_numbers):
out.append(perm)
print(out)
[(1,), (2,), (3,), (1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2), (1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
Use another variable to hold all the permutations. Extend it with the list returned by each call to permutations().
def fp(number):
# A Python program to print all
# permutations using library function
from itertools import permutations
all_perms = []
c= list(map(int,str(number)))
print(c, len(c))
i=1
while i <= len(c):
perm= permutations(c,i) #permuate the number c to the number of specified digits i
all_perms.extend(perm)
i+=1
# Print the obtained permutations
for i in all_perms:
print (i)
Imagine we have a list of stocks:
stocks = ['AAPL','GOOGL','IBM']
The specific stocks don't matter, what matters is that we have n items in this list.
Imagine we also have a list of weights, from 0% to 100%:
weights = list(range(101))
Given n = 3 (or any other number) I need to produce a matrix with every possible combinations of weights that sum to a full 100%. E.g.
0%, 0%, 100%
1%, 0%, 99%
0%, 1%, 99%
etc...
Is there some method of itertools that can do this? Something in numpy? What is the most efficient way to do this?
The way to optimize this isn't to figure out a faster way to generate the permutations, it's to generate as few permutations as possible.
First, how would you do this if you only wanted the combination that were in sorted order?
You don't need to generate all possible combinations of 0 to 100 and then filter that. The first number, a, can be anywhere from 0 to 100. The second number, b, can be anywhere from 0 to (100-a). The third number, c, can only be 100-a-b. So:
for a in range(0, 101):
for b in range(0, 101-a):
c = 100-a-b
yield a, b, c
Now, instead of generating 100*100*100 combination to filter them down to 100*50*1+1, we're just generating the 100*50*1+1, for a 2000x speedup.
However, keep in mind that there are still around X * (X/2)**N answers. So, computing them in X * (X/2)**N time instead of X**N may be optimal—but it's still exponential time. And there's no way around that; you want an exponential number of results, after all.
You can look for ways to make the first part more concise with itertools.product combined with reduce or accumulate, but I think it's going to end up less readable, and you want to be able to extend to any arbitrary N, and also to get all permutations rather than just the sorted ones. So keep it understandable until you do that, and then look for ways to condense it after you're done.
You obviously need to either go through N steps. I think this is easier to understand with recursion than a loop.
When n is 1, the only combination is (x,).
Otherwise, for each of the values a from 0 to x, you can have that value, together with all of the combinations of n-1 numbers that sum to x-a. So:
def sum_to_x(x, n):
if n == 1:
yield (x,)
return
for a in range(x+1):
for result in sum_to_x(x-a, n-1):
yield (a, *result)
Now you just need to add in the permutations, and you're done:
def perm_sum_to_x(x, n):
for combi in sum_to_x(x, n):
yield from itertools.permutations(combi)
But there's one problem: permutations permutes positions, not values. So if you have, say, (100, 0, 0), the six permutations of that are (100, 0, 0), (100, 0, 0), (0, 100, 0), (0, 0, 100), (0, 100, 0), (0, 0, 100).
If N is very small—as it is in your example, with N=3 and X=100—it may be fine to just generate all 6 permutations of each combination and filter them:
def perm_sum_to_x(x, n):
for combi in sum_to_x(x, n):
yield from set(itertools.permutations(combi))
… but if N can grow large, we're talking about a lot of wasted work there as well.
There are plenty of good answers here on how to do permutations without repeated values. See this question, for example. Borrowing an implementation from that answer:
def perm_sum_to_x(x, n):
for combi in sum_to_x(x, n):
yield from unique_permutations(combi)
Or, if we can drag in SymPy or more-itertools:
def perm_sum_to_x(x, n):
for combi in sum_to_x(x, n):
yield from sympy.multiset_permutations(combi)
def perm_sum_to_x(x, n):
for combi in sum_to_x(x, n):
yield from more_itertools.distinct_permutations(combi)
What you are looking for is product from itertools module
you can use it as shown below
from itertools import product
weights = list(range(101))
n = 3
lst_of_weights = [i for i in product(weights,repeat=n) if sum(i)==100]
What you need is combinations_with_replacement because in your question you wrote 0, 0, 100 which means you expect repetition, like 20, 20, 60 etc.
from itertools import combinations_with_replacement
weights = range(11)
n = 3
list = [i for i in combinations_with_replacement(weights, n) if sum(i) == 10]
print (list)
The above code results in
[(0, 0, 10), (0, 1, 9), (0, 2, 8), (0, 3, 7), (0, 4, 6), (0, 5, 5), (1, 1, 8), (1, 2, 7), (1, 3, 6), (1, 4, 5), (2, 2, 6), (2, 3, 5), (2, 4, 4), (3, 3, 4)]
Replace range(10), n and sum(i) == 10 by whatever you need.
This is a classic Stars and bars problem, and Python's itertools module does indeed provide a solution that's both simple and efficient, without any additional filtering needed.
Some explanation first: you want to divide 100 "points" between 3 stocks in all possible ways. For illustration purposes, let's reduce to 10 points instead of 100, with each one worth 10% instead of 1%. Imagine writing those points as a string of ten * characters:
**********
These are the "stars" of "stars and bars". Now to divide the ten stars amongst the 3 stocks, we insert two | divider characters (the "bars" of "stars and bars"). For example, one such division might look like this::
**|*******|*
This particular combination of stars and bars would correspond to the division 20% AAPL, 70% GOOGL, 10% IBM. Another division might look like:
******||****
which would correspond to 60% AAPL, 0% GOOGL, 40% IBM.
It's easy to convince yourself that every string consisting of ten * characters and two | characters corresponds to exactly one possible division of the ten points amongst the three stocks.
So to solve your problem, all we need to do is generate all possible strings containing ten * star characters and two | bar characters. Or, to think of this another way, we want to find all possible pairs of positions that we can place the two bar characters, in a string of total length twelve. Python's itertools.combinations function can be used to give us those possible positions, (for example with itertools.combinations(range(12), 2)) and then it's simple to translate each pair of positions back to a division of range(10) into three pieces: insert an extra imaginary divider character at the start and end of the string, then find the number of stars between each pair of dividers. That number of stars is simply one less than the distance between the two dividers.
Here's the code:
import itertools
def all_partitions(n, k):
"""
Generate all partitions of range(n) into k pieces.
"""
for c in itertools.combinations(range(n+k-1), k-1):
yield tuple(y-x-1 for x, y in zip((-1,) + c, c + (n+k-1,)))
For the case you give in the question, you want all_partitions(100, 3). But that yields 5151 partitions, starting with (0, 0, 100) and ending with (100, 0, 0), so it's impractical to show the results here. Instead, here are the results in a smaller case:
>>> for partition in all_partitions(5, 3):
... print(partition)
...
(0, 0, 5)
(0, 1, 4)
(0, 2, 3)
(0, 3, 2)
(0, 4, 1)
(0, 5, 0)
(1, 0, 4)
(1, 1, 3)
(1, 2, 2)
(1, 3, 1)
(1, 4, 0)
(2, 0, 3)
(2, 1, 2)
(2, 2, 1)
(2, 3, 0)
(3, 0, 2)
(3, 1, 1)
(3, 2, 0)
(4, 0, 1)
(4, 1, 0)
(5, 0, 0)
So, I have four lists. Two hold x coordinates (foodX and newPosXArray) and the other two hold y coordinates (foodX and newPosYArray). Both the food and the newPos arrays are of different dimensions because I have multiple "food sources", and multiple objects searching for the food.
I want to write an if statement that does something when the objects get to within a certain distance of the food.
My attempt with any()
if any(np.sqrt((newPosXArray[u]-foodX[t])**2 + (newPosYArray[u]-foodY[t])**2) <= 0.2 for t in zip(food[0], food[1]) for u in zip(newPosXArray, newPosYArray)):
#dosomething
I am getting an error TypeError: list indices must be integers, not tuple
Edit:
maybe I am misunderstanding zip(). I was assuming that it condenses this
if any(np.sqrt((newPosXArray[u]-foodX[t])**2 + (newPosYArray[u]-foodY[t])**2) <= 0.2 for t in foodX for t in foodY for u in newPosXArray for u in newPosYArray):
Typical Values of what I am working with
foodX = [5,5,-5,-5]
foodY = [5,-5,5,-5]
In [65]: newPosXArray
Out[65]:
[-0.012880860643600167,
-0.566815786730638,
0.7905336304903405,
0.09006991095474826,
0.26518652615441063,
0.3161232055076695,
0.681255361368023,
-0.6849985596071202,
0.7140832628874829,
0.4958515031060564]
In [66]: newPosYArray
Out[66]:
[-0.41112817779983235,
-0.08554837651693648,
0.8743935617169996,
-0.9384733737088207,
0.02423386678116546,
-0.3735855691077572,
-0.5251118585489394,
0.3950871276165102,
0.9892320167752822,
-0.7342372054958279]
of course, none of these values will return true in the if statement because none are within a 0.2 radius of any of the food coordinates
Just to be clear nested loops and zip do not do the same thing:
>>> [(i, j) for i in range(3) for j in range(3)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
Whereas:
>>> list(zip(range(3), range(3)))
[(0, 0), (1, 1), (2, 2)]
itertools.product does the same as next for loops:
>>> import itertools
>>> list(itertools.product(range(3), range(3)))
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
You don't index into the array when you iterate over an array, e.g:
>>> food = [1,2,3,4]
>>> [f+1 for f in food] # Note you did [food[f] for f in food]
[2,3,4,5]
So fixing your second example looks like:
if any(np.sqrt((u_x-t_x)**2 + (u_y-t_y)**2) <= 0.2
for t_x in foodX for t_y in foodY for u_x in newPosXArray for u_y in newPosYArray):
But this iterates foodY for every foodX so assume you do want these to be zipped which would be written:
if any(np.sqrt((u_x-t_x)**2 + (u_y-t_y)**2) <= 0.2
for t_x, t_y in zip(foodX, foodY) for u_x, u_y in zip(newPosXArray, newPosYArray)):
Or using itertools.product (which personally I don't find any easier to follow in this instance:
import itertools
if any(np.sqrt((u_x-t_x)**2 + (u_y-t_y)**2) <= 0.2
for (t_x, t_y), (u_x, u_y) in itertools.product(zip(foodX, foodY), zip(newPosXArray, newPosYArray))):
You try to get food[][z] but z is tuple (from zip method ), perhaps you meant to do
food[0][z[0]]
Of course, Pythonic way is to just use complex numbers instead of those 2D data separated along the wrong axis. But if you really need to use that data format, I guess
any(math.hypot(X-x,Y-y)<.2 for X,Y in zip(foodX,foodY) for x,y in zip(posX,posY))
will do what you want.
The problem is that u and t are tuples because zip places x and y side by side in tuple format. You need to say food[0][t[1]]andfood[1][t[0]] where you intend to use the list values as coordinates.
From what it sees, you are trying to give it an x,(x,y) coordinate instead of an x,y coordinate.
This holds where you are using u as a list index as well.
if any(np.sqrt((u[0] - t[0]) ** 2 + (u[1] - t[1]) ** 2) <= 0.2 for t in zip(foodX,foodY) for u in zip(newPosXArray,newPosYArray))
From values given it evaluates to False until the 0.2 value gets to 5, which sounds weird given how low your initial value was, but that's how the distance formula works.
Alternatively for readability you could change u to new and t to current.
I am having trouble finding a way to do this in a Pythonic way. I assume I can use itertools somehow because I've done something similar before but can't remember what I did.
I am trying to generate all non-decreasing lists of length L where each element can take on a value between 1 and N. For example if L=3 and N=3 then [1,1,1],[1,1,2],[1,1,3],[1,2,2],[1,2,3], etc.
You can do this using itertools.combinations_with_replacement:
>>> L, N = 3,3
>>> cc = combinations_with_replacement(range(1, N+1), L)
>>> for c in cc: print(c)
(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 works because c_w_r preserves the order of the input, and since we're passing a nondecreasing sequence in, we only get nondecreasing tuples out.
(It's easy to convert to lists if you really need those as opposed to tuples.)
I just found this instruction
itertools.product(*[(0, 1)] * n)
posted by PAG.
Can someone explain how it works?
I am trying to find a way of doing permutations without repetition of n tuples in 3 bags
and I only can use itertools if I want. Thanks
[(0, 1)] is a list of a single tuple of the numbers 0 and 1.
[(0, 1)] * n duplicates the tuple inside of the list, so we get
[(0, 1), (0, 1), ..., (0, 1), (0, 1)]
Then, if we look at the itertools.product function, we want to pass in each of those tuples as single arguments. So, we use the *-operator to unpack our list into arguments to the itertools.product function. So, our function is equivalent to:
itertools.product((0, 1), (0, 1), ..., (0, 1), (0, 1))
which computes all permutations of the n 0s and 1s.
Note that itertools.product takes a repeat parameter, which should be used to do this sort of thing:
itertools.product((0, 1), repeat=n)
To do permutations, you can use the itertools.permutations function:
def pick_into_three_bags(n):
return itertools.permutations(range(n), 3)