A combination algorithm in python - python

import copy
def combine(l, n):
answers = []
one = [0] * n
def next_c(li = 0, ni = 0):
if ni == n:
answers.append(copy.copy(one))
return
for k in range(li, len(l)):
one[ni] = l[k]
next_c(k+1, ni+1)
next_c()
return answers
print(combine([1,2,3,4],2))
Recently I found this code on internet. It works very well. However, I do not know the details that how does it work? So can anybody tell me how does it work? And how to understand a recursive code quickly? Thanks very much

As #AChampion said, You could also get the combinations with the itertools library, which is much easier to understand:
import itertools
def combine2(lst, n):
return [list(x) for x in itertools.combinations(lst, n)]
Output:
>>> combine2([1,2,3,4],2)
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

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

Sticky index reference while inserting into 2D list in Python

While attempting to implement a function that produces all permutations given a list of integers, I'm seeing this behavior where the inserts are not occurring as expected.
My code:
def permute(nums):
perms = [[]]
for i in range(len(nums)):
new_perms = perms * (i + 1)
for j in range(len(new_perms)):
new_perms[j].insert(j % len(perms), nums[i])
perms = new_perms
return perms
When calling permute([1, 2, 3]) I'm expecting the perms to grow like:
[[]]
[[1]]
[[2, 1], [1, 2]
[[3, 2, 1], [1, 3, 2], [2, 1, 3], [3, 1, 2], [2, 3, 1], [1, 2, 3]
However, by the second iteration of the interior loop with new_perms: [[1], [1]] I'm expecting it to grow to [[2, 1], [1, 2]], instead I'm getting [[2,1],[2,1]] and then [[2,2,1],[2,2,1]]. On each iteration of the j loop, the number is getting inserted into the current j position of all values of the list simultaneously on each iteration. Not what I was trying to do or expecting.
Ultimately, the code outputs:
[[3,3,3,3,3,3,2,2,1],[3,3,3,3,3,3,2,2,1],[3,3,3,3,3,3,2,2,1],[3,3,3,3,3,3,2,2,1],[3,3,3,3,3,3,2,2,1],[3,3,3,3,3,3,2,2,1]]
Either this is some subtle reference behavior (yay, learn something new!) or I'm just having a really dumb day ;) Any help appreciated.
PLEASE NOTE: I'm NOT asking for help with an alternate or optimal permutations function! I'm trying to figure out why this particular code is behaving in an unexpected way. Thank you.
Ok, learned a good one here. DEEPCOPY!
import copy
def permute(nums):
perms = [[]]
for i in range(len(nums)):
len_perms = len(perms)
old_perms = perms
perms = []
for _ in range(i+1):
perms += copy.deepcopy(old_perms)
for j in range(len(perms)):
perms[j].insert(j // len_perms, nums[i])
return perms

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 does 'yield' work in this permutation generator?

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.

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

Categories