Related
I'm writing recursive code to reverse a list in-place. This is what I have, but the code fails at assigning the slice to the input list, giving the error 'NoneType' object is not iterable:
def reverse(a):
if len(a) == 1:
return
temp = a[0]
a[0] = a[-1]
a[-1] = temp
a[1:-1] = reverse(a[1:-1])
return a
I have read that slices are not objects, so I attempted to use list() and range() to convert the returned slice, but by doing that, I still get the same error. Is there a way of assigning a slice to another slice of an array?
All your problem is return which sends None and gives 'NoneType' object is not iterable
You need return a. And rest of code start working.
BTW: You could use <= 1 instead of == 1 to work with empty list too.
def reverse(a):
if len(a) <= 1: # <-- `<=` to work with empty list
return a # <-- has to return `a`
temp = a[0]
a[0] = a[-1]
a[-1] = temp
#a[0], a[-1] = a[-1], a[0]
a[1:-1] = reverse(a[1:-1])
return a
# --- tests ---
print(reverse([1,2,3,4,5]))
print(reverse([1]))
print(reverse([]))
EDIT: To replace elements you can also use
a[0], a[-1] = a[-1], a[0]
EDIT: Using slice to create list with single element and list + list to join lists you can do:
def reverse(a):
if len(a) <= 1:
return a
return a[-1:] + reverse(a[1:-1]) + a[:1]
EDIT: You can even write it as lambda function (if you like functional programming which often uses recursion)
reverse = lambda a: a if len(a) <= 1 else a[-1:] + reverse(a[1:-1]) + a[:1]
# --- tests ---
print(reverse([1,2,3,4,5]))
print(reverse([1,2,3,4]))
print(reverse([1]))
print(reverse([]))
Reverse in-place
We will just keep on swapping start and end of list till both of them meet
def reverse(a,start=0,end=len(a)-1):
if start==end:return
a[start],a[end] = a[end],a[start]
reverse(a,start+1,end-1)
# return a # if it is inplace no need to return
a = [1,2,3,4,5]
reverse(a)
print(a)
[5, 4, 3, 2, 1]
I guess maybe this algorithm would be a bit closer to what you have in mind:
Here we are passing an index to the function you've already designed.
We would then increment that index using (-~index or simply index + 1).
def reverse(a, index=0):
if len(a) == 0:
return []
if len(a) == 1:
return a
if index == len(a) - 1:
return a
temp = a[0]
a[0] = a[-1]
a[-1] = temp
return reverse(a, index + 1)
print(reverse([1, 2, 3, 5, 6, 7, 8, 9]))
print(reverse([100, 90, -10, 1200, 1, 2, 3, 5, 6, 7, 8, 9]))
The three statements that you already have in your code are much more "algorithmic" (in all languages) than using Python's swap (a, b = b, a):
temp = a[0]
a[0] = a[-1]
a[-1] = temp
Output
[9, 2, 3, 5, 6, 7, 8, 1]
[9, 90, -10, 1200, 1, 2, 3, 5, 6, 7, 8, 100]
def merge (l1,l2):
if l1 and l2:
if l1 == [] and l2 == []:
return []
if l1[0] > l2[0]:
l1, l2 = l2, l1
return [l1[0]] + merge(l1[1:], l2)
return l1 + l2
def sort(l):
x = len(l) / 2
x = int(x)
y = merge(l[0:x], l[x+1:])
return y
I need to write a recursive function named sort; it is passed any unordered list (all int or all str) and it returns a new list that contains every value from its argument list, but in sorted/non-descending order. But I cannot call any of Python’s functions/methods that perform sorting.
also, For any list that has at least 2 values, I have to break the list in half and recursively call sort to sort each smaller list, I have to use the merge function, written above, to merge these two sorted lists returned from these recursive calls
merge is a function to combine and sort two list
merge([1,3,5,8,12],[2,3,6,7,10,15]) returns [1,2,3,3,5,6,7,8,10,12,15].
For example, calling sort([4,5,3,1,6,7,2]) would call sort recursively on the lists [4,5,3] and [1,6,7,2]), returning the lists [3,4,5] and [1,2,6,7] respectively, which when merged would return the list [1,2,3,4,5,6,7].
My function got the following error
39 *Error: sort([1,2,3,4,5,6,7]) -> [1, 2, 3, 5, 6, 7] but should -> [1, 2, 3, 4, 5, 6, 7]
40 *Error: sort([7,6,5,4,3,2,1]) -> [3, 2, 1, 7, 6, 5] but should -> [1, 2, 3, 4, 5, 6, 7]
41 *Error: sort([4,5,3,1,2,7,6]) -> [2, 4, 5, 3, 7, 6] but should -> [1, 2, 3, 4, 5, 6, 7]
42 *Error: sort([1,7,2,6,3,5,4]) -> [1, 3, 5, 4, 7, 2] but should -> [1, 2, 3, 4, 5, 6, 7]
What is wrong with me sort method? can someone help me to fix it? thanks in advance.
Three problems:
Your y = merge(l[0:x], l[x+1:]) loses l[x], make it y = merge(l[:x], l[x:]).
It doesn't sort the halves, so make it y = merge(sort(l[:x]), sort(l[x:])).
You have no base case, stopping the recursion when there's nothing to do.
Corrected and simplified a bit:
def sort(l):
if len(l) <= 1:
return l
x = len(l) // 2
return merge(sort(l[:x]), sort(l[x:]))
The // is integer division so you don't need the extra int(...). And no point in creating that y variable.
Btw, the if l1 == [] and l2 == []: test inside if l1 and l2: is pointless (if l1 and l2 were [], you wouldn't get inside the if l1 and l2: block in the first place), so you can remove it.
One more thing: While your merge function isn't wrong, it's slow. Every l1[1:] takes time proportional to the length of l1. You'd better uses indexes, like for example in Huy Vo's answer.
Ok basically everything you're doing is redundant.
list1 = [1,3,5,8,12]
list2 = [2,3,6,7,10,15]
list3 = list1 + list2 # Merges lists
list3_sorted = sorted(list3) # Sorts them
Also a little bonus, if you have a list of lists or tuples and you want to sort by an index of each of those
from operator import itemgetter
list = [(2,6), (3,4)]
list_sorted = sorted( list, key=itemgetter(1) ) # Will sort by index 1 of each item.
Edit: I now realise that you can't use any built in functions, give me a little to mess around and see if I can figure something out
What you need is a merge sort, I believe there are multiple merge sort pseudocodes on the internet.
Anyway, here is a version of mine in Python 3:
def mergesort(lst):
if len(lst) < 2:
return lst
else:
middle = len(lst) // 2
# recursion, baby
left_half = mergesort(lst[:middle])
right_half = mergesort(lst[middle:])
return merge(left_half, right_half)
def merge(left, right):
result = []
i, j = 0, 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
elif left[i] > right[j]:
result.append(right[j])
j += 1
result += left[i:] + right[j:]
return result
This question already has answers here:
Delete from a list while iterating [duplicate]
(3 answers)
Closed 6 years ago.
I'm in a online course at edx of Python and I have to do this little program, I think that the function is right but it has the bug when a element is deleted from the List suddenly the next element is not consider into the test.
def f(i):
return i + 2
def g(i):
return i > 5
def applyF_filterG(L, f, g):
"""
Assumes L is a list of integers
Assume functions f and g are defined for you.
f takes in an integer, applies a function, returns another integer
g takes in an integer, applies a Boolean function,
returns either True or False
Mutates L such that, for each element i originally in L, L contains
i if g(f(i)) returns True, and no other elements
Returns the largest element in the mutated L or -1 if the list is empty
"""
# Your code here
i = 0
if len(L) == 0:
return -1
while i < len(L):
if not g(f(L[i])):
del L[i]
i += 1
return max(L)
If I try with this example L = [0, -10, 5, 6, -4, -2], the value of L should be L = [5,6] but the result is this [-10, 5, 6, -2] the element -10 is skipped when the 0 was deleted and the same happens with -4 and -2. Please help, I don't know how can solve this.
Try not to iterate through the list that you mutate inside the loop in Python. In this example, the index order changed after you delete the element. I created a copy of L before iterating it and it does the trick.
def applyF_filterG(L, f, g):
copied_L = L[:]
for i in copied_L:
if not g(f(i)):
L.remove(i)
if len(L)==0:
return -1
else:
return max(L)
The problem:
You're deleting elements from the list as you iterate over the list. Due to that, i no longer refers to the right element in the list.
To illustrate this problem, here's a sample run through your code.
For this example we are going to assume that the if statement deletes an element if it's value is even.
I also assume that i has already been initialized.
L = [1, 2, 6, 3, 4]
iteration 1
i == 0, L[i] == 1, we don't delete the element.
L == [1, 2, 6, 3, 4]
iteration 2
i == 1, L[i] == 2, element is deleted.
L == [1, 6, 3, 4]
iteration 3
i == 2, L[i] == 3, we don't delete the element.
L == [1, 6, 3, 4]
# Did you notice that we just skipped checking the 6 because its index moved?!
iteration 4
i == 3, L[i] == 4, element is deleted.
L == [1, 6, 3]
We're finished!
There's a couple of ways to do this. Although #Meerness already supplied one way to do it, here's another way you could do it just for completeness.
i = len(L) - 1
if i == -1:
return -1
while i >= 0:
if not g(f(L[i])):
del L[i]
i -= 1
How this works:
In this way of doing it, you count down from the topmost index. That way, the deletion of an element doesn't affect the indexes of elements that you still haven't checked.
My explanation of this way of doing it is a slightly reworded version of the comment by #JohnColeman.
JSYK, though, I had already written this solution before I saw his comment so I didn't borrow the idea from him -- I only borrowed his explanation. :)
Here's an example of what happens when we count down instead of counting up:
L = [1, 2, 6, 3, 4]
iteration 1
i == 4, L[i] == 4, element is deleted.
L == [1, 2, 6, 3]
iteration 2
i == 3, L[i] == 3, we don't delete the element.
L == [1, 2, 6, 3]
iteration 3
i == 2, L[i] == 6, element is deleted.
L == [1, 2, 3]
iteration 4
i == 1, L[i] == 2, element is deleted.
L == [1, 3]
iteration 5
i == 0, L[i] == 1, we don't delete the element.
L == [1, 3]
We're finished!
PS: Examples automatically generated with python3 script. :)
Mutating a list as you're iterating over it is a bad idea and will lead to unexpected behaviour. In general, you're better off creating a new list to work on.
In this specific case, your code can be fixed with an easy modification. Only iterate the index if you aren't deleting an element, so as not to skip ahead in the list.
while i < len(L):
if not g(f(L[i])):
del L[i]
else:
i += 1
def applyF_filterG(L, f, g):
"""
Assumes L is a list of integers
Assume functions f and g are defined for you.
f takes in an integer, applies a function, returns another integer
g takes in an integer, applies a Boolean function,
returns either True or False
Mutates L such that, for each element i originally in L, L contains
i if g(f(i)) returns True, and no other elements
Returns the largest element in the mutated L or -1 if the list is empty
"""
M =[]
for i in range(len(L)):
if g(f(L[i])):
M.append(L[i])
L = M[:]
if len(L)==0:
return -1
else:
return max(L)
I'm struggling to understand why my QuickSort returns the sorted values correctly, but the resulting array is not sorted correctly.
def qSort(array):
n = len(array)
if (n == 1 or n ==0):
return array
p_index = partition(array)
p_value = array[p_index]
return(qSort(array[0:p_index]) + [p_value] + qSort(array[p_index+1:n]))
def partition(array):
pivot = array[0]
i = 1
for j in xrange(1,len(array)):
print j
if array[j] < pivot:
tmp = array[j]
array[j] = array[i]
array[i]=tmp
i += 1
tmp = array[i-1]
array[i-1] = pivot
array[0] = tmp
return i-1
Here is some sample output:
>>> q = [5,4,3,2,1]
>>> qSort(q)
[1, 2, 3, 4, 5]
>>> q
[1, 4, 3, 2, 5]
Thank you in advance!
In Python, slicing and combining lists create new lists. If you want your recursive calls to operate on a single list in place, pass the list and the bounds into the call, and don't return anything from the function. Something like:
def qsort(array, low, high):
if high-low < 2:
return
# Choose pivot, do partition within bounds
if partition > low:
qsort(array, low, partition)
if partition < high:
qsort(array, partition+1, high)
Then just call qsort(a, 0, len(a)) to sort the array.
This is because you are making up a new list in your return statement.
return(qSort(array[0:p_index]) + [p_value] + qSort(array[p_index+1:n]))
If the qSort function reaches a base case, it returns a list, which is concatenated with [p_value] and returned as a list. You do not make changes to the passed in list anywhere.
When you call your qSort function recursively, you are giving it a slice of the list and the function returns the list in the base case which you then append to the pivot and the other recursive call, hence generating a new list.
See what is happening by changing your qSort function to
def qSort(array):
n = len(array)
if (n == 1 or n ==0):
return array
p_index, array = partition(array)
p_value = array[p_index]
returnVal = qSort(array[0:p_index]) + [p_value] + qSort(array[p_index+1:n])
print "Returning:", returnVal, "Original Array:", array
return returnVal
Output -
>>> q = [5,4,3,2,1]
>>> qSort(q)
Returning: [2, 3] Original Array: [2, 3]
Returning: [2, 3, 4] Original Array: [2, 3, 4]
Returning: [1, 2, 3, 4] Original Array: [1, 4, 3, 2]
Returning: [1, 2, 3, 4, 5] Original Array: [1, 4, 3, 2, 5]
[1, 2, 3, 4, 5]
To reflect the changes in your original list, you have the option of doing q = qSort(q).
P.S - Setting up a random index instead of the first value would be better for your quicksort function. See the bit here on Choice of Pivots.
apply the function back to q
q = qSort(q)
If you want to return the array and also sort in place you should before returning make the array equal to the result and not make a new one. You can do that by changing your return statement to:
array[:] = qSort(array[0:p_index]) + [p_value] + qSort(array[p_index+1:n])
return array
Note that
array = qSort(array[0:p_index]) + [p_value] + qSort(array[p_index+1:n])
does not work either, because the lhs variable will be treated as a local variable.
I need to verify if a list is a subset of another - a boolean return is all I seek.
Is testing equality on the smaller list after an intersection the fastest way to do this? Performance is of utmost importance given the number of datasets that need to be compared.
Adding further facts based on discussions:
Will either of the lists be the same for many tests? It does as one of them is a static lookup table.
Does it need to be a list? It does not - the static lookup table can be anything that performs best. The dynamic one is a dict from which we extract the keys to perform a static lookup on.
What would be the optimal solution given the scenario?
>>> a = [1, 3, 5]
>>> b = [1, 3, 5, 8]
>>> c = [3, 5, 9]
>>> set(a) <= set(b)
True
>>> set(c) <= set(b)
False
>>> a = ['yes', 'no', 'hmm']
>>> b = ['yes', 'no', 'hmm', 'well']
>>> c = ['sorry', 'no', 'hmm']
>>>
>>> set(a) <= set(b)
True
>>> set(c) <= set(b)
False
Use set.issubset
Example:
a = {1,2}
b = {1,2,3}
a.issubset(b) # True
a = {1,2,4}
b = {1,2,3}
a.issubset(b) # False
The performant function Python provides for this is set.issubset. It does have a few restrictions that make it unclear if it's the answer to your question, however.
A list may contain items multiple times and has a specific order. A set does not. Additionally, sets only work on hashable objects.
Are you asking about subset or subsequence (which means you'll want a string search algorithm)? Will either of the lists be the same for many tests? What are the datatypes contained in the list? And for that matter, does it need to be a list?
Your other post intersect a dict and list made the types clearer and did get a recommendation to use dictionary key views for their set-like functionality. In that case it was known to work because dictionary keys behave like a set (so much so that before we had sets in Python we used dictionaries). One wonders how the issue got less specific in three hours.
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]
all(x in two for x in one)
Explanation: Generator creating booleans by looping through list one checking if that item is in list two. all() returns True if every item is truthy, else False.
There is also an advantage that all return False on the first instance of a missing element rather than having to process every item.
Assuming the items are hashable
>>> from collections import Counter
>>> not Counter([1, 2]) - Counter([1])
False
>>> not Counter([1, 2]) - Counter([1, 2])
True
>>> not Counter([1, 2, 2]) - Counter([1, 2])
False
If you don't care about duplicate items eg. [1, 2, 2] and [1, 2] then just use:
>>> set([1, 2, 2]).issubset([1, 2])
True
Is testing equality on the smaller list after an intersection the fastest way to do this?
.issubset will be the fastest way to do it. Checking the length before testing issubset will not improve speed because you still have O(N + M) items to iterate through and check.
One more solution would be to use a intersection.
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]
set(one).intersection(set(two)) == set(one)
The intersection of the sets would contain of set one
(OR)
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]
set(one) & (set(two)) == set(one)
Set theory is inappropriate for lists since duplicates will result in wrong answers using set theory.
For example:
a = [1, 3, 3, 3, 5]
b = [1, 3, 3, 4, 5]
set(b) > set(a)
has no meaning. Yes, it gives a false answer but this is not correct since set theory is just comparing: 1,3,5 versus 1,3,4,5. You must include all duplicates.
Instead you must count each occurrence of each item and do a greater than equal to check. This is not very expensive, because it is not using O(N^2) operations and does not require quick sort.
#!/usr/bin/env python
from collections import Counter
def containedInFirst(a, b):
a_count = Counter(a)
b_count = Counter(b)
for key in b_count:
if a_count.has_key(key) == False:
return False
if b_count[key] > a_count[key]:
return False
return True
a = [1, 3, 3, 3, 5]
b = [1, 3, 3, 4, 5]
print "b in a: ", containedInFirst(a, b)
a = [1, 3, 3, 3, 4, 4, 5]
b = [1, 3, 3, 4, 5]
print "b in a: ", containedInFirst(a, b)
Then running this you get:
$ python contained.py
b in a: False
b in a: True
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]
set(x in two for x in one) == set([True])
If list1 is in list 2:
(x in two for x in one) generates a list of True.
when we do a set(x in two for x in one) has only one element (True).
Pardon me if I am late to the party. ;)
To check if one set A is subset of set B, Python has A.issubset(B) and A <= B. It works on set only and works great BUT the complexity of internal implementation is unknown. Reference: https://docs.python.org/2/library/sets.html#set-objects
I came up with an algorithm to check if list A is a subset of list B with following remarks.
To reduce complexity of finding subset, I find it appropriate to
sort both lists first before comparing elements to qualify for
subset.
It helped me to break the loop when value of element of second list B[j] is greater than value of element of first list A[i].
last_index_j is used to start loop over list B where it last left off. It helps avoid starting comparisons from the start of
list B (which is, as you might guess unnecessary, to start list B from index 0 in subsequent iterations.)
Complexity will be O(n ln n) each for sorting both lists and O(n) for checking for subset.
O(n ln n) + O(n ln n) + O(n) = O(n ln n).
Code has lots of print statements to see what's going on at each iteration of the loop. These are meant for understanding
only.
Check if one list is subset of another list
is_subset = True;
A = [9, 3, 11, 1, 7, 2];
B = [11, 4, 6, 2, 15, 1, 9, 8, 5, 3];
print(A, B);
# skip checking if list A has elements more than list B
if len(A) > len(B):
is_subset = False;
else:
# complexity of sorting using quicksort or merge sort: O(n ln n)
# use best sorting algorithm available to minimize complexity
A.sort();
B.sort();
print(A, B);
# complexity: O(n^2)
# for a in A:
# if a not in B:
# is_subset = False;
# break;
# complexity: O(n)
is_found = False;
last_index_j = 0;
for i in range(len(A)):
for j in range(last_index_j, len(B)):
is_found = False;
print("i=" + str(i) + ", j=" + str(j) + ", " + str(A[i]) + "==" + str(B[j]) + "?");
if B[j] <= A[i]:
if A[i] == B[j]:
is_found = True;
last_index_j = j;
else:
is_found = False;
break;
if is_found:
print("Found: " + str(A[i]));
last_index_j = last_index_j + 1;
break;
else:
print("Not found: " + str(A[i]));
if is_found == False:
is_subset = False;
break;
print("subset") if is_subset else print("not subset");
Output
[9, 3, 11, 1, 7, 2] [11, 4, 6, 2, 15, 1, 9, 8, 5, 3]
[1, 2, 3, 7, 9, 11] [1, 2, 3, 4, 5, 6, 8, 9, 11, 15]
i=0, j=0, 1==1?
Found: 1
i=1, j=1, 2==1?
Not found: 2
i=1, j=2, 2==2?
Found: 2
i=2, j=3, 3==3?
Found: 3
i=3, j=4, 7==4?
Not found: 7
i=3, j=5, 7==5?
Not found: 7
i=3, j=6, 7==6?
Not found: 7
i=3, j=7, 7==8?
not subset
Below code checks whether a given set is a "proper subset" of another set
def is_proper_subset(set, superset):
return all(x in superset for x in set) and len(set)<len(superset)
In python 3.5 you can do a [*set()][index] to get the element. It is much slower solution than other methods.
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]
result = set(x in two for x in one)
[*result][0] == True
or just with len and set
len(set(a+b)) == len(set(a))
Here is how I know if one list is a subset of another one, the sequence matters to me in my case.
def is_subset(list_long,list_short):
short_length = len(list_short)
subset_list = []
for i in range(len(list_long)-short_length+1):
subset_list.append(list_long[i:i+short_length])
if list_short in subset_list:
return True
else: return False
Most of the solutions consider that the lists do not have duplicates. In case your lists do have duplicates you can try this:
def isSubList(subList,mlist):
uniqueElements=set(subList)
for e in uniqueElements:
if subList.count(e) > mlist.count(e):
return False
# It is sublist
return True
It ensures the sublist never has different elements than list or a greater amount of a common element.
lst=[1,2,2,3,4]
sl1=[2,2,3]
sl2=[2,2,2]
sl3=[2,5]
print(isSubList(sl1,lst)) # True
print(isSubList(sl2,lst)) # False
print(isSubList(sl3,lst)) # False
Since no one has considered comparing two strings, here's my proposal.
You may of course want to check if the pipe ("|") is not part of either lists and maybe chose automatically another char, but you got the idea.
Using an empty string as separator is not a solution since the numbers can have several digits ([12,3] != [1,23])
def issublist(l1,l2):
return '|'.join([str(i) for i in l1]) in '|'.join([str(i) for i in l2])
If you are asking if one list is "contained" in another list then:
>>>if listA in listB: return True
If you are asking if each element in listA has an equal number of matching elements in listB try:
all(True if listA.count(item) <= listB.count(item) else False for item in listA)