Printing all subsets with only a given length in python - python

I have the task to print all subsets of given length, I have created the functions to print out the all the subsets. Everything works fine the subsets are generated, but the output is wrong for each call. For example if I call print(get_subsets([1,2,3],2)) the output is [1,2] [1,3] [2,3] and [3]. Of course 3 is not supposed to be there and I can't figure out why. Any help will be appreciated and feedback of course.
def get_subsets(nums, k):
all_subsets = []
_gen_subsets(nums =nums,curr_idx =0,curr_subset=[],
all_subsets=all_subsets)
for curr_subset in all_subsets:
if len(curr_subset) > k or len(curr_subset) < k:
all_subsets.remove(curr_subset)
return all_subsets
def _gen_subsets(nums,curr_idx, curr_subset, all_subsets):
if curr_idx >= len(nums):
all_subsets.append(curr_subset)
else:
itr_subset = curr_subset.copy()
itr_subset.append(nums[curr_idx])
_gen_subsets(nums=nums,
curr_idx=curr_idx+1,
curr_subset=itr_subset,
all_subsets=all_subsets)
_gen_subsets(nums=nums,
curr_idx=curr_idx+1,
curr_subset=curr_subset,
all_subsets=all_subsets)

You are trying to iterate through a list from which you are also removing elements. This messed up the position and index of the list of subsets and gave you the wrong output.
Change the function get_subsets to this:
def get_subsets(nums, k):
all_subsets = []
_gen_subsets(nums =nums,curr_idx =0,curr_subset=[],
all_subsets=all_subsets)
final_subset = all_subsets.copy()
for n in all_subsets:
if len(n) != k:
final_subset.remove(n)
return final_subset
Or this:
def get_subsets(nums, k):
all_subsets = []
_gen_subsets(nums =nums,curr_idx =0,curr_subset=[],
all_subsets=all_subsets)
all_subsets = [n for n in all_subsets if len(n) == k]
return all_subsets

Your problem is caused by this:
for curr_subset in all_subsets:
if len(curr_subset) > k or len(curr_subset) < k:
all_subsets.remove(curr_subset)
You are removing array elements while iterating. That leads to the following scenario: Initially you have array [X1, X2, X3].
In the first iteration, you remove the first element X1. Then the second element X2 becomes the first element and therefore, on the second iteration, the third element X3 (which has become the second element) is traversed and X2 is skipped. That is why [3] is not removed from your array.
To solve this, you can change get_subsets function as below:
def get_subsets(nums, k):
all_subsets = []
_gen_subsets(nums =nums,curr_idx =0,curr_subset=[],
all_subsets=all_subsets)
all_subsets = [subset for subset in all_subsets if len(subset) == k]
return all_subsets

maybe thes two will help you?
def Combinations(l, d):
if (d <= 1) or (len(l) <= 1):
return [[i] for i in l]
else:
result = []
for i in range(len(l)):
c = l[i]
lx = l[:i]+l[i+1:]
for cmb in Combinations(lx, d-1):
result.append([c]+cmb)
return result
print(*Combinations([1, 2, 3, 4, 5, 6], 2), sep='\n')
def Allocations(l, d):
if (d <= 1) or (len(l) <= 1):
return [[i] for i in l]
else:
result = []
for i in range(len(l)+1-d):
c = l[i]
lx = l[i+1:]
for cmb in Allocations(lx, d-1):
result.append([c]+cmb)
return result
print(*Allocations([1, 2, 3, 4, 5, 6], 3), sep='\n')

Related

Find the longest increasing subarray

For example : input = [1,2,3,1,5,7,8,9] , output = [1,5,7,8,9]
find out the longest continuous increasing subarray
I have tried on my own like this :
def longsub(l):
newl = []
for i in range(len(l)) :
if l[i] < l[i+1] :
newl.append(l[i])
else :
newl = []
return newl
But it would get error since the list index out of range. (It could not get the value after last value)
def longsub(l):
newl = []
for i in range(len(l)) :
if l[i] > l[i-1] :
newl.append(l[i])
else :
newl = []
return newl
And then I did this, but I would get the result without the first value of increasing subarray.
What should I rectify my code? Thanks!
Suppose that you had this helper at your disposal:
def increasing_length_at(l, i):
"""Returns number of increasing values found at index i.
>>> increasing_length_at([7, 6], 0)
1
>>> increasing_length_at([3, 7, 6], 0)
2
"""
val = l[i] - 1
for j in range(i, len(l)):
if l[j] <= val: # if non-increasing
break
val = l[j]
return j - i
How could you use that as part of a solution?
You could use 2 loops (first to iterate over the input and the second loop to iterate from the index of the first loop until the end):
inp = [1,2,3,1,5,7,8,9]
output = [1,5,7,8,9]
i, res = 0, []
while i < len(inp):
tempResult = [startNum := inp[i]] # Python>3.8: Walrus operator
for j in range(i+1, len(inp)):
if startNum > inp[j]:
i = j-1 # skip already compared items!
break
tempResult.append(startNum := inp[j]) # Python>3.8: Walrus operator
if len(tempResult) > len(res):
res = tempResult
i += 1
print(res, res == output)
Out:
[1, 5, 7, 8, 9] True
Firstly, you can use len(l) - 1 to avoid the IndexError. However, your approach is invalid since this would just return the last increasing sub. Here's my approach:
def longsub(l):
res, newl = [], []
for i in range(len(l)-1):
if l[i] < l[i+1]:
newl.append(l[i])
else:
newl.append(l[i])
res.append(newl)
newl = []
if newl: res.append(newl)
return max(res, key=len)
input = [1,2,3,4,5,1,5,7,8,9]
print(longsub(input))
Output:
>>> [1, 2, 3, 4, 5]

How do I find the index of variable b from list a?

How do I find the index of variable b from list a?
I suspect that the problem is in the data types.
a=[-202516736, '-202516736', '13886', '678280946', '14514', '330251838', '14511', '639566631', '14510', '542472303', '14506']
b=['678280946']
a_INT = [int(item) for item in a]
b_INT = [int(item) for item in b]
j = 0
while True:
try:
i = a_INT.index(b_INT, j)
print(i)
j = i + 1
except:
break
Let's take this a step further and add another value to the b list and also add a duplicate in the a list. Then:
a=[-202516736, '-202516736', '13886', '678280946', '14514', '678280946', '330251838', '14511', '639566631', '14510', '542472303', '14506']
b=['678280946', 13886]
ai = list(map(int, a))
for n in map(int, b):
offset = 0
r = []
while True:
try:
i = ai[offset:].index(n)
r.append(offset+i)
offset += i + 1
except ValueError:
break
print(f'{n} occurs at {r}')
Output:
678280946 occurs at [3, 5]
13886 occurs at [2]
Version 2:
The first piece of code is functionally correct. However, it could be very inefficient if the list being searched is very large.
Python's built-in sort function is very fast. So, let's build a list of 2-tuples each made up of a value from the list and its original index. Then sort the new list. Now that it's sorted we can perform a binary search and move on from there.
Added some more values to the OP's original list for demonstration purposes:
a = [-202516736, '-202516736', '13886', '678280946', '14514', '678280946',
'330251838', '14511', '639566631', '14510', '542472303', '14506', '678280946']
b = ['678280946', 13886, 14514, '-202516736', 99]
def bsearch(lst, x):
L = 0
R = len(lst) - 1
while L <= R:
m = (L + R) // 2
if (v := lst[m][0]) == x:
return m
if v < x:
L = m + 1
else:
R = m - 1
return -1
def findall(list_, n):
templist = sorted((v, i) for i, v in enumerate(list_))
result = None
if (i := bsearch(templist, n)) >= 0:
result = [templist[i][1]]
for j in range(i-1, -1, -1):
if templist[j][0] != n:
break
result.append(templist[j][1])
for j in range(i+1, len(templist)):
if templist[j][0] != n:
break
result.append(templist[j][1])
return result
ai = list(map(int, a))
for n in map(int, b):
print(f'{n} -> {findall(ai, n)}')
Output:
678280946 -> [5, 3, 12]
13886 -> [2]
14514 -> [4]
-202516736 -> [0, 1]
99 -> None
a=[-202516736, '-202516736', '13886', '678280946', '14514', '330251838', '14511', '639566631', '14510', '542472303', '14506']
b=['678280946']
for item in b:
print(a.index(item))
Since b has only one element the output is 3.

Python : iterating over a list

With my code I would except that after looping one time he will jump to the next number in the list, but he doesn't. Anyone knows what is wrong with my code?
for j in range(len(k)):
s = [int(i) for i in str(k[j])]
This two lines of code are part of a bigger question I am solving.
def kaprekarseries(n):
"""
>>> kaprekar_series(677)
[677, 99, 0]
>>> kaprekar_series(9876)
[9876, 3087, 8352, 6174]
>>> kaprekar_series(55500)
[55500, 54945, 50985, 92961, 86922, 75933, 63954, 61974, 82962]
"""
k = list()
k.append(n)
count = 0
while count < 10:
for j in range(len(k)):
s = [int(i) for i in str(k[j])]
m = sorted(s, key=int, reverse=True)
m2 = int(''.join(str(i) for i in m))
l = sorted(s, key=int)
l2 = int(''.join(str(i) for i in l))
g = m2 - l2
k.append(g)
if [item for item in k if k.count(item) <= 1]:
count += 1
else:
return k
Your list k has just one element being appended prior to starting the loop. Therefore it is list of length of one, therefore it runs just once.
Rethink your algorythm.

Find/extract a sequence of integers within a list in python

I want to find a sequence of n consecutive integers within a sorted list and return that sequence. This is the best I can figure out (for n = 4), and it doesn't allow the user to specify an n.
my_list = [2,3,4,5,7,9]
for i in range(len(my_list)):
if my_list[i+1] == my_list[i]+1 and my_list[i+2] == my_list[i]+2 and my_list[i+3] == my_list[i]+3:
my_sequence = list(range(my_list[i],my_list[i]+4))
my_sequence = [2,3,4,5]
I just realized this code doesn't work and returns an "index out of range" error, so I'll have to mess with the range of the for loop.
Here's a straight-forward solution. It's not as efficient as it might be, but it will be fine unless you have very long lists:
myarray = [2,5,1,7,3,8,1,2,3,4,5,7,4,9,1,2,3,5]
for idx, a in enumerate(myarray):
if myarray[idx:idx+4] == [a,a+1,a+2,a+3]:
print([a, a+1,a+2,a+3])
break
Create a nested master result list, then go through my_sorted_list and add each item to either the last list in the master (if discontinuous) or to a new list in the master (if continuous):
>>> my_sorted_list = [0,2,5,7,8,9]
>>> my_sequences = []
>>> for idx,item in enumerate(my_sorted_list):
... if not idx or item-1 != my_sequences[-1][-1]:
... my_sequences.append([item])
... else:
... my_sequences[-1].append(item)
...
>>> max(my_sequences, key=len)
[7, 8, 9]
A short and concise way is to fill an array with numbers every time you find the next integer is the current integer plus 1 (until you already have N consecutive numbers in array), and for anything else, we can empty the array:
arr = [4,3,1,2,3,4,5,7,5,3,2,4]
N = 4
newarr = []
for i in range(len(arr)-1):
if(arr[i]+1 == arr[i+1]):
newarr += [arr[i]]
if(len(newarr) == N):
break
else:
newarr = []
When the code is run, newarr will be:
[1, 2, 3, 4]
#size = length of sequence
#span = the span of neighbour integers
#the time complexity is O(n)
def extractSeq(lst,size,span=1):
lst_size = len(lst)
if lst_size < size:
return []
for i in range(lst_size - size + 1):
for j in range(size - 1):
if lst[i + j] + span == lst[i + j + 1]:
continue
else:
i += j
break
else:
return lst[i:i+size]
return []
mylist = [2,3,4,5,7,9]
for j in range(len(mylist)):
m=mylist[j]
idx=j
c=j
for i in range(j,len(mylist)):
if mylist[i]<m:
m=mylist[i]
idx=c
c+=1
tmp=mylist[j]
mylist[j]=m
mylist[idx]=tmp
print(mylist)

Calculating combinations of length k from a list of length n using recursion

I need to generate all the combinations with length k from a list of length n, and I must do it using recursion.
For Example:
INPUT: choose_sets([1,2,3,4],3)
OUTPUT: [[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
INPUT: choose_sets([1,2,3,4],2)
OUTPUT: [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
I'm stuck implementing this in code, so I would be happy for some help.
This is my code so far (I'm missing something just don't know what):
def choose_sets(lst,k):
if k == len(lst):
return lst
if k == 0:
return []
if k > len(lst):
return []
sets=[]
sub_lst=lst[:]
sub_lst.remove(sub_lst[0])
a= choose_sets(sub_lst,k-1)
for i in a:
i.append(lst[0])
sets.append(a)
b= choose_sets(sub_lst,k)
sets.append(b)
return sets
You can get solution from Generator for permutations, combinations, selections of a sequence (Python recipe)
def xuniqueCombinations(items, n):
if n==0: yield []
else:
for i in xrange(len(items)):
for cc in xuniqueCombinations(items[i+1:],n-1):
yield [items[i]]+cc
>>> def xuniqueCombinations(items, n):
... if n==0: yield []
... else:
... for i in xrange(len(items)):
... for cc in xuniqueCombinations(items[i+1:],n-1):
... yield [items[i]]+cc
...
>>> for x in xuniqueCombinations( [1,2,3,4],2):
... print x
[1, 2]
[1, 3]
[1, 4]
[2, 3]
[2, 4]
[3, 4]
Edited 4 year later (7/12/2015)
To run it on Python3 just change xrange to range, Python3's range is Python2's xrange.. Thanks #ederollora to notice me.
Give a look to this solution:
def choose_sets(mylist,length):
mylen = len(mylist)
if length == mylen:
return [mylist]
if length == 1:
return [[i] for i in mylist]
if length > mylen:
return []
ToRet = []
for k in xrange(mylen):
if mylen - k + 1> length :
for j in choose_sets(mylist[k+1:],length-1):
New = [mylist[k]]
New.extend(j)
ToRet.append(New)
return ToRet
print choose_sets([1,2,3,4,5],3)
there are more elegant ways, but this should be ok as homework...
This is in Java, and I can't guarantee it works 100% properly, but based on quick prototyping seemed to work ok. Hope this helps a bit in any case.
public void choose_sets(int values[], int count) {
int perm[] = new int[count];
choose_sets(values, 0, perm, 0, count);
}
public void choose_sets(int[] values, int valuesIdx, int[] perm,
int permIdx, int count) {
if (permIdx == count) {
// At this point perm -array contains single permutation
// of length ´count´.
} else {
for (int i = valuesIdx; i < values.length; ++i) {
perm[permIdx] = values[i];
choose_sets(values, i + 1, perm, permIdx + 1, count);
}
}
}
basically you need to use the following recursion:
f(k,n) = append_to_each( f(k-1,n-1), n) | f(k,n-1)
def combinations(lst,k):
n = len(lst)
if n == k:
return [set(lst)]
if k == 1:
return [set([lst[i]]) for i in range(n)]
v1 = combinations(lst[:-1], k-1)
v1new = [ i.add(lst[n-1]) for i in v1]
v2 = combinations(lst[:-1], k)
return v1+v2
You are almost there, just a few minor things. The algorithm is basically correct, but
if k == len(lst):
return lst
This has the wrong type. The return type is not a list of thing, but a list of (list of thing), so that should be
if k == len(lst):
return [lst]
Next,
if k == 0:
return []
Every list has exactly one nonempty sublist, the empty list, so that ought to be
if k == 0:
return [[]]
For the rest,
if k > len(lst):
return []
is completely correct.
sets=[]
sub_lst=lst[:]
sub_lst.remove(sub_lst[0])
That is correct but could be put more succinctly as
sub_lst = lst[1:]
Now, another type mix-up:
a= choose_sets(sub_lst,k-1)
for i in a:
i.append(lst[0])
sets.append(a)
That sets.append(a) puts a into one slot of sets, you want to concatenate the two lists, sets = sets + a. And if you would like the combinations in the order in which elements appear in the list, instead of i.append(lst[0]), you should append [lst[0]] + i to sets in the loop, but that's a matter of inclination.
b= choose_sets(sub_lst,k)
sets.append(b)
Again, do not append, but concatenate here,
sets = sets + b

Categories