How does 'yield' work in this permutation generator? - python

def perm_generator(lst):
if len(lst) == 1:
yield lst
else:
for i in range(len(lst)):
for perm in perm_generator(lst[:i] + lst[i+1:]):
yield [lst[i]] + perm
This code has been bugging me, since I don't understand how the yields connect to each other. My understanding was that yield acts like a return, but it stops temporarily until it is called again. How do these yields work?

It might help seeing a version that doesn't use generators:
def perm_generator(lst):
res = []
if len(lst) == 1:
return [lst]
else:
for i in range(len(lst)):
for perm in perm_generator(lst[:i] + lst[i+1:]):
res.append([lst[i]] + perm)
return res
gen = perm_generator([1,2,3])
print gen # prints [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
As you can see - this is not a "find and replace" of "return" with "yield". In the "return" version we needs to accumulate the result while in the "yield" version all that needs to be done is to "yield" the current permutation.

Related

How to find subsets from a set that product equals the target?

Let us say we have a list and target of:
list: [1,2,3,4,5] and target: 20
and we want to find total combinations to reach this, with multiplication, which is:
[1,4,5], [4,5], [2,5,2], [1,2,2,5]
I did this code, but I can't seem to know how to remove the ones so I have them too, I mean that I receive:
[1,4,5], [1,2,2,5].
But without [1], I can't seem to get it, I tried to "cheat" somehow to get it, but I can't since my code doesn't fit for it...
def Coin(target, lst, temp=[], comb=[]):
if target == 1:
comb.append(temp)
return comb
if len(lst) == 0:
return []
if target >= lst[0]:
if lst[0] > 1:
take=Coin(target/lst[0],lst,temp+[lst[0]],comb)
dont_take=Coin(target,lst[1:],temp,comb)
else:
take_1 = Coin(target, lst[1:], temp + [1], comb)
return take_1
return take
return comb
print(Coin(20, [1,2,3,4,5], [], []))
[[1, 2, 2, 5], [1, 4, 5]]
How to add the parts without 1? I don't need a solution, since as said, not homework, but practice for exam. Just a clue will be enough, I want to find it myself, but I need a clue for it.
Maybe you should combine
if lst[0] > 1:
else:
together, that means for 1 we should also decide whether take it or not.
This is much easier to do with a generator using yield than a function using return.
The difference is that you can only return once, while you can yield any number of times (including 0 if there are no solutions).
If you wish to return a list, you still can, like this:
def coin (target, lst):
return list(_coin(target, lst, 0)
def _coin (target, lst, i):
...
If that doesn't matter, the generator saves memory by not having to generate the whole list at once. And you simply:
def coin (target, lst, i=0):
... # Use yield as often as you want, all will be returned.
Second, you are running into the most common gotcha in Python. You are using mutable objects as defaults. If you're going to continue with your current approach you need to:
def coin(target, lst, temp=None, comb=None):
if temp is None:
temp = []
if comb is None:
comb = []
...
And third, you should get in the habit of following standard style conventions. In many ways, what the convention is doesn't matter that much. But that everyone is on the same page, does. Therefore you should try to follow the most common Python convention. In which the function should be named coin instead of Coin.
Edit:
The rules for the question is:
Positive integers only ( 0 not allowed ).
Number can appear once only ( at input ).
can not repeat numbers on list.
EG: [1,2,3,4], n=12 >> [1,12], [12,1], [3,4], [2,6], [1,3,4], [1,2,6].
NOT: [1,2,2,3], [2,2,3]
That is all I guess.
def coin_change_MULTI(num, lst):
if num == 1 and 1 not in lst:
return []
return Coin_MULTI(num, sorted(lst), [], [])
def Coin_MULTI(target, lst, temp=[], comb=[]):
if target == 1:
if big_than_target(1, lst):
return [[1]]
comb.append(temp)
return comb
if len(lst) == 0: return []
if target >= lst[0]:
if lst[0] > 1:
take=Coin_MULTI(target/lst[0],lst[1:],temp+[lst[0]],comb)
dont_take=Coin_MULTI(target,lst[1:],temp,comb)
return comb
else:
take_1 = Coin_MULTI(target, lst[1:], temp + [1], comb)
dont_take_1 = Coin_MULTI(target, lst[1:], temp, comb)
return comb
return take + dont_take
return comb
print(coin_change_MULTI(12, [2,4,6,12,7,3, 1]))
print(coin_change_MULTI(1, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(1, [2,4,6,12,7,3]))
print(coin_change_MULTI(100, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(576, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(12096, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(0, [2,4,6,12,7,3,1]))
print((coin_change_MULTI(24, [2,4,6,12,7,3,1])))
[[1, 2, 6], [1, 3, 4], [1, 12], [2, 6], [3, 4], [12]]
[[1]]
[]
[]
[[1, 2, 4, 6, 12], [2, 4, 6, 12]]
[[1, 2, 3, 4, 6, 7, 12], [2, 3, 4, 6, 7, 12]]
[]
[[1, 2, 3, 4], [1, 2, 12], [1, 4, 6], [2, 3, 4], [2, 12], [4, 6]]
Process finished with exit code 0

Combinations of restricted set of integers

How to generate the list of combinations with allowed_ints that sums to goal ?
Examples:
allowed_ints=[1,2], goal=4
combinations = [[1,1,1,1],[1,1,2],[2,1,1],[2,2],[1,2,1]]
allowed_ints=[5, 6], goal=13
combinations = []
What I've made so far don't works.
def combinations(allowed_ints, goal):
if goal > 0:
for i in allowed_ints:
for p in combinations(allowed_ints, goal-i):
yield [i] + p
else:
yield []
print list(combinations([1, 2],3))
[[1, 1, 1], [1, 1, 2], [1, 2], [2, 1], [2, 2]] # not what I want
Using you function try this:
def combinations(allowed_ints, goal):
if goal > 0:
for i in allowed_ints:
for p in combinations(allowed_ints, goal-i):
if sum([i] + p) == goal:
yield [i] + p
else:
yield []
print list(combinations([1, 2],3))
Outputs:
[[1, 1, 1], [1, 2], [2, 1]]
I know you've already selected an answer, but I wanted to offer an alternative that's not as similar to your code. If you wanted to use recursion, I would suggest something like this:
def combinations(allowed_ints, list, goal):
# base case: you're done
if sum(list) == goal:
print list
# if overshoot, stop here
elif sum(list) > goal:
pass
else:
for i in allowed_ints:
new_list = list[:]
new_list.append(i)
combinations(allowed_ints, new_list, goal)
combinations([1, 2],[],4)
It's no better than the suggested answer, but uses a different approach.
You should include 3 conditions like
goal == 0 then recursion worked
goal < 0 then recursion failed
goal >= 0 and list elements end then recursion failed
By the way you can do it all using recursion. No need of loop, recursion can also do loop

How to find all partial solutions of a list recursively

I have a box with a maximum weight capacity. I am given a list of weights of the items that I am supposed to fit in the box. I need all solutions that get to, or as close as possible to, the maximum capacity. What I mean is that I should not be able to add another item to any of the partial solutions without going over the maximum capacity.
If I am given the following list of weights:
[1,2,3,4,5]
If the maximum capacity is 9, the solutions should be (I may have missed one, but you get the point):
[[4,3,2], [5,3,1], [5,4], [3,2,1], [4,2,1], [4,3,1], [5,3]]
Here's my recursive algorithm, I think I am getting close but I can't figure out how to fix it.
def findSubset(alist, maxim):
if maxim <= 0:
return [[]]
if len(alist) == 0:
return []
alist2 = alist[1:]
include = findSubset(alist2, maxim-alist[0])
for s in include:
s.append(alist[0])
return include + findSubset(alist2, maxim)
Current output is:
[[4, 3, 2, 1], [5, 3, 2, 1], [5, 4, 2, 1], [5, 4, 3, 1], [5, 3, 1], [5, 4, 1], [4, 3, 2], [5, 3, 2], [5, 4, 2], [5, 4, 3], [5, 4]]
My advice would be to iterate on all element of the list, while building a list of possible solutions
if the element is < maxim, recurse on a sublist on following elements for maxim - elt, and for each element of the result, append the element to it and append all to the resulting list
if the element is == maxim, add the singleton list containing the element to the resulting list
Code is:
def findSubset(alist, maxim):
res = []
if maxim <= 0:
return [[]]
if len(alist) == 0:
return []
for i, elt in enumerate(alist):
if elt < maxim:
res.extend([ [elt] + l for l in findSubset(alist[i+1:], maxim-elt)])
elif elt == maxim:
res.append([elt])
return res
It gives
>>> findSubset(lst, 9)
[[1, 3, 5], [2, 3, 4], [4, 5]]
Above code only gives exact solutions. In case where there are no exact solution, it should be extended to give best approaching solutions:
def findApproachSubset(alist, maxim):
for i in range(maxim, 0, -1):
res = findSubset(alist, i)
if len(res) > 0:
return res
return []
For example:
>>> findSubset([1, 4, 5, 6], 8)
[]
>>> findApproachSubset([1, 4, 5, 6], 8)
[[1, 6]]
because here the best solution is for 7 instead of 8.
This is written using Haskell. As it seems that this is homework, there is no point giving you the complete answer.
We use three separate functions, the function f can be made recursive as it is just map
xs = [1, 2, 3, 4, 5]
perms [] = []
perms xs = xs : perms (tail xs)
All you need to know is that : means cons i.e 1:[2] = [1,2]
In python, we could write it as:
def perms(xs):
if xs:
return [xs] + perms(xs[1:])
else:
return []
so perms xs is just [[1,2,3,4,5],[2,3,4,5],[3,4,5],[4,5],[5]]
takeWhileLessThan _ [] = []
takeWhileLessThan n (x:xs) =
if n-x < 0 then [] else (x : takeWhileLessThan (n-x) xs)
and takeWhileLessThan uses recursion. It operates on a single list, keeping track of the current sum.
f n xs = map (takeWhileLessThan n) (perms xs)
> [[1,2,3],[2,3,4],[3,4],[4,5],[5]]
The final function maps the recursive function over the list of lists. If you then wanted all values and wanted it to be recursive, just write another function...
if you are allowed for loops, then this method would work well. The only recursion is the main function.
def f(n, xs, solutions):
if xs:
m = n
ys = []
for x in xs:
if (m-x) < 0:
break
else:
ys.append(x)
m = m-x
solutions.append(ys)
return f(n, xs[1:], solutions)
else:
return solutions
which gives:
>>> f(9, [1,2,3,4,5], [])
[[1, 2, 3], [2, 3, 4], [3, 4], [4, 5], [5]]
def recursive_function(current_node: int, nodes_left: list, maximum: int, trace: list) -> list:
sum_so_far = sum(trace)
if current_node is not None:
trace.append(current_node)
else:
current_node = 0
if sum_so_far + current_node > maximum:
# That means, that current trace is already to big
# no need to search further this path
return False
elif sum_so_far + current_node == maximum:
# Bingo! The perfect set of nodes!
# No need to look further this path
return {tuple(sorted(trace))}
else:
# We still haven't reached maximum value
# let's check if we can pack some more nodes to trace
results = set()
for node in nodes_left:
nodes_copy = nodes_left.copy()
nodes_copy.remove(node)
traces = recursive_function(node, nodes_copy, maximum, trace.copy())
if traces:
# This path gave us some legitimate results, we collect them
results.update(traces)
if results:
# At least one possible path gave us results, we pass them on
return results
# There was nothing left in nodes_left that we could add and not
# cross the maximum limit. The path leading to this moment fits
# our requirements.
return {tuple(sorted(trace))}
def findSubset(alist, maxim) -> list:
results = recursive_function(None, alist, maxim, [])
return results
Why do I do this {tuple(sorted(trace))}? So I don't keep different permutations of same results in my memory.
Time cost: n!
Memory cost: n

Python - Inner list concatenation

Ok, while I maybe wrong, I have the feeling I can solve this without the help of a function.
With this kind of input list:
input=[[1,2,3],[4,5],[7],[5,4,6],[5,4]]
I want a resulting list where inner lists sharing a common element are concatenated:
result=[[1,2,3],[4,5,6],[7]]
I already found the
if any(x in result[j] for x in input[i])
syntax that seems helpful for my problem, but now I'm stuck.
[EDIT]
one of my trials:
input=[[1,2,3],[4,5],[7],[5,4,6],[5,4]]
result=[input[0]]
for i in range(len(input)):
for j in range(len(result)):
if result[j] != input[i]:
if any(x in result[j] for x in input[i]):
result[j].extend(input[i])
result[j]=list(set(result[j]))
print "concatenate ",result[j], input[i]
break
else :
result.append(input[i])
print "append ", result[j], input[i]
break
>>> (bad) result
[[1, 2, 3], [4, 5], [7], [5, 4, 6], [5, 4]]
While if I initialize result with result=[input[-1]] it works:
input=[[1,2,3],[4,5],[7],[5,4,6],[5,4]]
result=[input[-1]]
for i in range(len(input)):
for j in range(len(result)):
if result[j] != input[i]:
if any(x in result[j] for x in input[i]):
result[j].extend(input[i])
result[j]=list(set(result[j]))
print "concatenate ",result[j], input[i]
break
else :
result.append(input[i])
print "append ", result[j], input[i]
break
>>> (good) result
[[4, 5, 6], [1, 2, 3], [7]]
This is a more simple and well-performing approach to solve your problem:
def concat(myinput) :
myinput_copy = [set(elem) for elem in myinput]
result = []
i = 0
# use `while` to avoid `for` problems (you're deleting list elems)
while i < len(myinput_copy) :
result.append(myinput_copy[i])
j = i + 1
while j < len(myinput_copy) :
if result[i] & myinput_copy[j] :
result[i] |= myinput_copy[j]
del myinput_copy[j]
j = i + 1
else :
j += 1
i += 1
return result
print concat([[1,2,3],[4,5],[7],[5,4,6],[5,4]])
It's about two times faster than J.F. Sebastian's answer to the other question, even if the other question wanted a different sorting.
I used myinput because input is a built-in function, and it's better to not masque it.
About sets and their operators: http://docs.python.org/3/tutorial/datastructures.html#sets (Python 3)
By the way, your solution is wrong, user1189129. Try your code with this input to see the problem: input = [[1, 2, 3], [1, 8], [1, 9], [4, 5], [7], [5, 4, 6], [5, 4]]
You can convert your list to plain list (extract all variables from sublist into a bigger list) and then split them into the chunks:
def chunks(l, n):
for i in xrange(0, len(l), n):
yield l[i:i+n]
def plain(lst):
return set([x for y in lst for x in y])
lst = [[1,2,3],[4,5],[7],[5,4,6],[5,4]]
print list(chunks(tuple(plain(l)), 3))

How to print data from list of list with recursion

Python. It is given list of list. How to make function which will data from list represent like 'moreline' string' like that every data will be shown in its new line, and in front of it are so many '*' as many as depth of data is.
Example : we have list [2, 4, [[3, 8], 1]] and now function has to make and return string, which function 'print' prints it out like this:
* 2
* 4
*** 3
*** 8
** 1
I have made for now just this and its not working
def Function(List):
s=''
Count=0
for element in List:
Count+=1
if type(element)==type([]):
s+=Function(element)
else:
s+=Count*'*'+str(element)+('\n')
return s
if a replace return (s) with print(s) it report me an error ... but if i return s and than myself print that string it works normally , but once again not how it should
>>> Function([2, 4, [[3, 8], 1]])
'*2\n*4\n*3\n*8\n**1\n'
>>> print('*2\n*4\n*3\n*8\n**1\n')
*2
*4
*3
*8
**1
Where is the problem, I can't find it. What should I replace, remove, etc.?
def r(l, depth=0, ret=[]):
if isinstance(l,list):
for i in l:
r(i, depth+1)
else:
ret.append('*' * depth + str(l))
return ret
print '\n'.join(r([2, 4, [[3, 8], 1]]))
output:
*2
*4
***3
***8
**1
You need to pass on count to recursive calls; local variables do not magically transfer to new function invocations:
def format_nested(lst, depth=1):
s = []
for element in lst:
if isinstance(element, list):
s.append(print_nested(element, depth + 1))
else:
s.append('{0} {1}\n'.format(depth * '*', element))
return ''.join(s)
I addressed various other issues with the code:
Use descriptive function and argument names. Function is not a great name.
Use a list to build up elements of a string, then use str.join(); it is faster than building up strings by concatenation.
Only increment the depth counter when recursing, not for every element in current level of the list.
Use isinstance() to test for specific types.
String formatting makes it a little easier to build strings together with constant elements, such as a space and a newline.
Demo:
>>> format_nested([2, 4, [[3, 8], 1]])
'* 2\n* 4\n*** 3\n*** 8\n** 1\n'
>>> print format_nested([2, 4, [[3, 8], 1]])
* 2
* 4
*** 3
*** 8
** 1
Often it's easier to express these things as a generator
L = [2, 4, [[3, 8], 1]]
def nest_gen(L):
if isinstance(L, list):
for i in L:
for j in nest_gen(i):
yield "*"+str(j)
else:
yield L
for row in nest_gen(L):
print(row)
In Python3.3+ you can use yield from
L = [2, 4, [[3, 8], 1]]
def nest_gen(L):
if isinstance(L, list):
yield from ("*"+str(j) for i in L for j in nest_gen(i))
else:
yield L
for row in nest_gen(L):
print(row)
Instead of catenating strings over and over, you can yield the depth/item as a tuple
L = [2, 4, [[3, 8], 1]]
def nest_gen(L):
if isinstance(L, list):
yield from ((j+1, k) for i in L for j, k in nest_gen(i))
else:
yield 0, L
for item in nest_gen(L):
print("{:*>{}} {}".format('', *item))

Categories