Calculating subsets of a given list recursively - python

I want to build a recursive function that finds all the subsets of size 'k' of a given list with length n>=k>=0 and returns a list of those subsets.
example:
if the input list is [1,2,3,4] and k = 2 then the function will return
[[4,3],[2,4],[2,3],[1,4],[1,3],[1,2]]
notice that different arrangments of list is considered to be the same list.
I think that this kind of recursion should work:
return [lst[0]] + choose_sets(lst[1:],k-1) ¬¬and¬¬ choose_sets(lst[1:],k)
where choose_sets(lst,k) is the function.
Meaning:
input : [1,2,3,4] , k=3
calls:
[1] + [2,3,4],k=2 and [2,3,4], k=3
and so on...
can anyone guide me as to how I should call those 2 recursive calls 'at the same time' ?
and what should my 'exiting term' be?
Thanks.

Suppose you have a list of size n and you need all subsets of size k.
This is basically the same as:
For each element of the list,
create a new list without the element,
in the new list, find all the subsets of size k-1 (this is the recursive call),
and add the remove element to all the lists.
Now... this solution will have repetitions, for example, in the example you gave, you'll get both [4, 1] and [1, 4]. But it can be changed a little so that it will not create duplicate results.
edit
to handle duplications
def choose_sets(l, k):
if k == 0:
return [[]]
if len(l) == 0:
return []
l2 = l[1:]
subsets = choose_sets(l2, k-1)
for s in subsets:
s.append(l[0])
return subsets+ choose_sets(l2, k)

b = []
def abc(a,k):
if len(a)==k:
b.append(a)
return b
b.extend([a[:k-1]+[i] for i in a[k-1:]])
return abc(a[1:],k)
print abc([1,2,3,4,5],2)

Related

Method for indexing into a list by a number of dimensions

I am writing code that uses nested lists. My list structure is essentially 2 elements, but the second element comprises two individual elements and so on and so forth. The size of the list therefore grows as 2^n + 1. Anyway, for each recursion of a function I need to be able to access any given element at some 'depth'. Usually, this would be trivial as given,
list1 = [[0,1], [[1,2], [1,0]]]
the following code:
list1[1][1]
would return:
[1,0]
However, for an n dimensional nested list (I use the word 'dimension' fairly haphazardly given that this is a nested list and not an array) I would surely need to 'exponentiate' my [1] index in order to index into each progressively 'deeper' nested list. Is there a trivial way to do this?
Thanks in advance, your help is greatly appreciated.
Use recursion:
def get(li, k):
return get(li[1], k-1) if k > 0 else li
Or iteration:
def get(li, k):
for _ in range(k):
li = li[1]
return li
I created this, I tried to make it as self explanatory as possible:
my_list = [[8,9], [[0,1], [[1,2], [1,0]]]]
# dim=1 -> [8,9]
# dim=2 -> [0,1]
# dim=3 -> [1,2]
# dim=4 -> [1,0]
def get_value_by_dimension(dim :int, lst: list):
x = dim-1
if x==0:
return (lst[0])
else:
try:
return get_value_by_dimension(x, lst[1])
except:
return "Dim is to high!"
print(get_value_by_dimension(2, my_list))
# [0, 1]
print(get_value_by_dimension(1, my_list))
# [8, 9]
print(get_value_by_dimension(5, my_list))
# Dim is to high!

Pairwise multiplier list arguments Python

I'm trying to write a pairwise multiplier function which takes two arguments, both being lists. pairwise_multiply should return a new list with each of the elements in the two input lists multiplied together in a pairwise fashion. e.g.
result = pairwise_multiply([1, 2], [3, 4])
print(result)
> [3, 8]
This is my current function but I keep getting syntax errors:
def pairwise_multiply([l1], [l2]):
i = 0
while 1 <= len(l1):
lst = int(l1[i] * l2[i])
i = i + 1
return lst
In your code here -
def pairwise_multiply([l1], [l2]):
You don't need square brackets to pass lists as arguments. Replace it with -
def pairwise_multiply(l1, l2):
Another implementation, more pythonic would be to use list comprehension with zip -
[i*j for i, j in zip(l1, l2)]
What zip does is (from official documentation)-
Make an iterator that aggregates elements from each of the iterables.
Returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables. The iterator stops when the shortest input iterable is exhausted. With a single iterable argument, it returns an iterator of 1-tuples
There are some syntax and logic errors in this snippet.
def pairwise_multiply([l1], [l2]) As #FHTMitchell pointed out, you should cannot not use [...] when naming the arguments. This should be def pairwise_multiply(l1, l2)
while 1 <= len(l1) you mean i, not 1, right? Otherwise you will have an infinite loop. Also, since Python uses zero-based indexing, <= should become <.
You overwrite lst in every iteration. Your function will only return (if at all, see previous point) the result of the last multiplication.
Taking these into account, your code can be transformed to
def pairwise_multiply(l1, l2):
lst = []
i = 0
while i < len(l1):
lst.append(int(l1[i] * l2[i]))
i = i + 1
return lst
But it has many points of failure (for example, what if l1 and l2 are not the same length?), too long and not pythonic.
We can use zip and list comprehension like #ThatBird suggested in their answer.

Check number not a sum of 2 ints on a list

Given a list of integers, I want to check a second list and remove from the first only those which can not be made from the sum of two numbers from the second. So given a = [3,19,20] and b = [1,2,17], I'd want [3,19].
Seems like a a cinch with two nested loops - except that I've gotten stuck with break and continue commands.
Here's what I have:
def myFunction(list_a, list_b):
for i in list_a:
for a in list_b:
for b in list_b:
if a + b == i:
break
else:
continue
break
else:
continue
list_a.remove(i)
return list_a
I know what I need to do, just the syntax seems unnecessarily confusing. Can someone show me an easier way? TIA!
You can do like this,
In [13]: from itertools import combinations
In [15]: [item for item in a if item in [sum(i) for i in combinations(b,2)]]
Out[15]: [3, 19]
combinations will give all possible combinations in b and get the list of sum. And just check the value is present in a
Edit
If you don't want to use the itertools wrote a function for it. Like this,
def comb(s):
for i, v1 in enumerate(s):
for j in range(i+1, len(s)):
yield [v1, s[j]]
result = [item for item in a if item in [sum(i) for i in comb(b)]]
Comments on code:
It's very dangerous to delete elements from a list while iterating over it. Perhaps you could append items you want to keep to a new list, and return that.
Your current algorithm is O(nm^2), where n is the size of list_a, and m is the size of list_b. This is pretty inefficient, but a good start to the problem.
Thee's also a lot of unnecessary continue and break statements, which can lead to complicated code that is hard to debug.
You also put everything into one function. If you split up each task into different functions, such as dedicating one function to finding pairs, and one for checking each item in list_a against list_b. This is a way of splitting problems into smaller problems, and using them to solve the bigger problem.
Overall I think your function is doing too much, and the logic could be condensed into much simpler code by breaking down the problem.
Another approach:
Since I found this task interesting, I decided to try it myself. My outlined approach is illustrated below.
1. You can first check if a list has a pair of a given sum in O(n) time using hashing:
def check_pairs(lst, sums):
lookup = set()
for x in lst:
current = sums - x
if current in lookup:
return True
lookup.add(x)
return False
2. Then you could use this function to check if any any pair in list_b is equal to the sum of numbers iterated in list_a:
def remove_first_sum(list_a, list_b):
new_list_a = []
for x in list_a:
check = check_pairs(list_b, x)
if check:
new_list_a.append(x)
return new_list_a
Which keeps numbers in list_a that contribute to a sum of two numbers in list_b.
3. The above can also be written with a list comprehension:
def remove_first_sum(list_a, list_b):
return [x for x in list_a if check_pairs(list_b, x)]
Both of which works as follows:
>>> remove_first_sum([3,19,20], [1,2,17])
[3, 19]
>>> remove_first_sum([3,19,20,18], [1,2,17])
[3, 19, 18]
>>> remove_first_sum([1,2,5,6],[2,3,4])
[5, 6]
Note: Overall the algorithm above is O(n) time complexity, which doesn't require anything too complicated. However, this also leads to O(n) extra auxiliary space, because a set is kept to record what items have been seen.
You can do it by first creating all possible sum combinations, then filtering out elements which don't belong to that combination list
Define the input lists
>>> a = [3,19,20]
>>> b = [1,2,17]
Next we will define all possible combinations of sum of two elements
>>> y = [i+j for k,j in enumerate(b) for i in b[k+1:]]
Next we will apply a function to every element of list a and check if it is present in above calculated list. map function can be use with an if/else clause. map will yield None in case of else clause is successful. To cater for this we can filter the list to remove None values
>>> list(filter(None, map(lambda x: x if x in y else None,a)))
The above operation will output:
>>> [3,19]
You can also write a one-line by combining all these lines into one, but I don't recommend this.
you can try something like that:
a = [3,19,20]
b= [1,2,17,5]
n_m_s=[]
data=[n_m_s.append(i+j) for i in b for j in b if i+j in a]
print(set(n_m_s))
print("after remove")
final_data=[]
for j,i in enumerate(a):
if i not in n_m_s:
final_data.append(i)
print(final_data)
output:
{19, 3}
after remove
[20]

divide list using recursion

I am trying to implement this function using recursion, the function takes a function parameter f where when passed a value it will return as true or false. It should check all values in the list and store all true values in a list and false values in another list returning them in a tuple.
def divL(f, l):
if not l:
return ([],[])
else:
a = list()
b = list()
for i in range(len(l)):
if f(l[i]):
a.append(l[i])
else:
b.append(l[i])
return (a, b)
Recursive version.
But I agree with others that it is better to do this without recursion.
def divL(f, l):
if not l:
return ([],[])
else:
a, b = divL(f,l[1:])
if f(l[0]):
a.insert(0, l[0])
else:
b.insert(0, l[0])
return (a, b)
#-- test ---
def f(x): return x > 0
divL(f,[1,-2,3,4,2,-5])
([1, 3, 4, 2], [-2, -5])
To solve this problem recursively, then you can start with:
def div_l(fn, elements):
if not elements:
return ([], [])
else:
trues, falses = div_l(fn, elements[1:])
if fn(elements[0]):
trues.append(elements[0])
else:
falses.append(elements[0])
return (trues, falses)
One thing to note about your code, is that, at the moment, there are no recursive calls. In the code above, our base case is when the list of elements is empty (if not elements). Then, we constantly check the first element of the list to see if it satisfies fn, appending it to trues or falses appropriately. Then we pass all the elements of the list besides the first one (elements[1:]) to the function again and repeat until the list is empty.
Having said that, the problem does not seem to be recursive in nature. Why not just use list comprehensions:
a = [element for element in l if f(element)]
b = [element for element in l if not f(element)]
Also, names like a, b, l and f are not great because they say nothing about what they actually mean.
Regarding the way you have written your code so far, the Pythonic way to iterate through the elements of a list is not the same as languages like C/C++/Java/etc; you should rarely need to get list items using their index. Instead you can re-write your for statement as follows:
for element in l:
if f(element):
a.append(element)
else:
b.append(element)
P.S. I haven't tested the code above yet, but it should be a reasonable starting point.
Recursive function that takes split lists as parameters:
def _R(f, l, ts, fs):
if not l:
return ts, fs
if f(l[0]):
return _R(f, l[1:], ts + l[:1], fs)
return _R(f, l[1:], ts, fs + l[:1])
def R(f, l):
return _R(f, l, [], [])
print R( lambda x: x > 10, range(20) )

Sort list of lists by unique reversed absolute condition

Context - developing algorithm to determine loop flows in a power flow network.
Issue:
I have a list of lists, each list represents a loop within the network determined via my algorithm. Unfortunately, the algorithm will also pick up the reversed duplicates.
i.e.
L1 = [a, b, c, -d, -a]
L2 = [a, d, c, -b, -a]
(Please note that c should not be negative, it is correct as written due to the structure of the network and defined flows)
Now these two loops are equivalent, simply following the reverse structure throughout the network.
I wish to retain L1, whilst discarding L2 from the list of lists.
Thus if I have a list of 6 loops, of which 3 are reversed duplicates I wish to retain all three.
Additionally, The loop does not have to follow the format specified above. It can be shorter, longer, and the sign structure (e.g. pos pos pos neg neg) will not occur in all instances.
I have been attempting to sort this by reversing the list and comparing the absolute values.
I am completely stumped and any assistance would be appreciated.
Based upon some of the code provided by mgibson I was able to create the following.
def Check_Dup(Loops):
Act = []
while Loops:
L = Loops.pop()
Act.append(L)
Loops = Popper(Loops, L)
return Act
def Popper(Loops, L):
for loop in Loops:
Rev = loop[::-1]
if all (abs(x) == abs(y) for x, y in zip(loop_check, Rev)):
Loops.remove(loop)
return Loops
This code should run until there are no loops left discarding the duplicates each time. I'm accepting mgibsons answers as it provided the necessary keys to create the solution
I'm not sure I get your question, but reversing a list is easy:
a = [1,2]
a_rev = a[::-1] #new list -- if you just want an iterator, reversed(a) also works.
To compare the absolute values of a and a_rev:
all( abs(x) == abs(y) for x,y in zip(a,a_rev) )
which can be simplified to:
all( abs(x) == abs(y) for x,y in zip(a,reversed(a)) )
Now, in order to make this as efficient as possible, I would first sort the arrays based on the absolute value:
your_list_of_lists.sort(key = lambda x : map(abs,x) )
Now you know that if two lists are going to be equal, they have to be adjacent in the list and you can just pull that out using enumerate:
def cmp_list(x,y):
return True if x == y else all( abs(a) == abs(b) for a,b in zip(a,b) )
duplicate_idx = [ idx for idx,val in enumerate(your_list_of_lists[1:])
if cmp_list(val,your_list_of_lists[idx]) ]
#now remove duplicates:
for idx in reversed(duplicate_idx):
_ = your_list_of_lists.pop(idx)
If your (sub) lists are either strictly increasing or strictly decreasing, this becomes MUCH simpler.
lists = list(set( tuple(sorted(x)) for x in your_list_of_lists ) )
I don't see how they can be equivalent if you have c in both directions - one of them must be -c
>>> a,b,c,d = range(1,5)
>>> L1 = [a, b, c, -d, -a]
>>> L2 = [a, d, -c, -b, -a]
>>> L1 == [-x for x in reversed(L2)]
True
now you can write a function to collapse those two loops into a single value
>>> def normalise(loop):
... return min(loop, [-x for x in reversed(L2)])
...
>>> normalise(L1)
[1, 2, 3, -4, -1]
>>> normalise(L2)
[1, 2, 3, -4, -1]
A good way to eliminate duplicates is to use a set, we just need to convert the lists to tuples
>>> L=[L1, L2]
>>> set(tuple(normalise(loop)) for loop in L)
set([(1, 2, 3, -4, -1)])
[pair[0] for pair in frozenset(sorted( (c,negReversed(c)) ) for c in cycles)]
Where:
def negReversed(list):
return tuple(-x for x in list[::-1])
and where cycles must be tuples.
This takes each cycle, computes its duplicate, and sorts them (putting them in a pair that are canonically equivalent). The set frozenset(...) uniquifies any duplicates. Then you extract the canonical element (in this case I arbitrarily chose it to be pair[0]).
Keep in mind that your algorithm might be returning cycles starting in arbitrary places. If this is the case (i.e. your algorithm might return either [1,2,-3] or [-3,1,2]), then you need to consider these as equivalent necklaces
There are many ways to canonicalize necklaces. The above way is less efficient because we don't care about canonicalizing the necklace directly: we just treat the entire equivalence class as the canonical element, by turning each cycle (a,b,c,d,e) into {(a,b,c,d,e), (e,a,b,c,d), (d,e,a,b,c), (c,d,e,a,b), (b,c,d,e,a)}. In your case since you consider negatives to be equivalent, you would turn each cycle into {(a,b,c,d,e), (e,a,b,c,d), (d,e,a,b,c), (c,d,e,a,b), (b,c,d,e,a), (-a,-b,-c,-d,-e), (-e,-a,-b,-c,-d), (-d,-e,-a,-b,-c), (-c,-d,-e,-a,-b), (-b,-c,-d,-e,-a)}. Make sure to use frozenset for performance, as set is not hashable:
eqClass.pop() for eqClass in {frozenset(eqClass(c)) for c in cycles}
where:
def eqClass(cycle):
for rotation in rotations(cycle):
yield rotation
yield (-x for x in rotation)
where rotation is something like Efficient way to shift a list in python but yields a tuple

Categories