While loop conditions in: compound conditional expressions AND'd [python] - python

Apologies for the really trivial introductory level python question.
Currently working through Google Python tutorials and hit something which may trip me up if I don't nail it down - using and'd values as a compound condition for execution of a while loop.
Reading through it appears as if the while loop operates whilst the length of both lists are positive. So once the length of both lists == 0, then the while loop hits a 0 and terminates.
I'm unsure of how to parse this mentally - whether the condition is that once both lengths == 0 then the and statement and's 0 and 0, giving a negative condition and terminates.
Reading it through I parse it as while '5' and '6' (if for instance 5 and 6 are the len of lists). I've not come across use of a while loop in this way so far (only been going for a day or so).
Code bit I don't get (abstract lines)
while len(list1) and len(list2):
Code in context
def linear_merge(list1, list2):
result = []
while len(list1) and len(list2):
if list1[0] < list2[0]:
result.append(list1.pop(0))
else:
result.append(list2.pop(0))
result.extend(list1)
result.extend(list2)
return result
Thanks kindly.

while len(list1) and len(list2):
Will continue to loop while both list1 and list2 are not empty; if either list is empty, the loop will terminate.
(In a boolean context, any value except False, None, 0, "", or [] will evaluate as true.)

Quoting Built In Types Page on Official Python documentation:
x and y give the result according to: if x is false, then x, else y
Further on this page it is mentioned that:
This is a short-circuit operator, so it only evaluates the second argument if the first one is True
So in your question, it first evaluates len(list1). If it is positive, the first condition is True and next it evaluates the second condition. If that is also True (i.e. len(list2)>=1), it enters into the loop. While fundamentally it is an AND operation, it differs in the sense that we don't need to evaluate the second condition, if the first one is False. This can be very helpful in certain cases, when the second condition may involve time consuming calculations.

The key to understanding this is the pop statement.
Basically, the function merges two already-sorted lists into one list containing all elements of both lists. It is a part of the mergesort algorithm. This works as follows:
As long as both lists contain remaining elements, repeat the following loop
select the smaller element from the head of the lists (i.e. the first elements)
remove the element from the respective list and add it to the result list
repeat
Then, at most one of the lists still contains elements. Add the remaining elements of that list to the result.

Related

Merge sorting algorithm in Python for two sorted lists - trouble constructing for-loop

I'm trying to create an algorithm to merge two ordered lists into a larger ordered list in Python. Essentially I began by trying to isolate the minimum elements in each list and then I compared them to see which was smallest, because that number would be smallest in the larger list as well. I then appended that element to the empty larger list, and then deleted it from the original list it came from. I then tried to loop through the original two lists doing the same thing. Inside the "if" statements, I've essentially tried to program the function to append the remainder of one list to the larger function if the other is/becomes empty, because there would be no point in asking which elements between the two lists are comparatively smaller then.
def merge_cabs(cab1, cab2):
for (i <= all(j) for j in cab1):
for (k <= all(l) for l in cab2):
if cab1 == []:
newcab.append(cab2)
if cab2 == []:
newcab.append(cab1)
else:
k = min(min(cab1), min(cab2))
newcab.append(k)
if min(cab1) < min(cab2):
cab1.remove(min(cab1))
if min(cab2) < min(cab1):
cab2.remove(min(cab2))
print(newcab)
cab1 = [1,2,5,6,8,9]
cab2 = [3,4,7,10,11]
newcab = []
merge_cabs(cab1, cab2)
I've had a bit of trouble constructing the for-loop unfortunately. One way I've tried to isolate the minimum values was as I wrote in the two "for" lines. Right now, Python is returning "SyntaxError: invalid syntax," pointing to the colon in the first "for" line. Another way I've tried to construct the for-loop was like this:
def merge_cabs(cabs1, cabs2):
for min(i) in cab1:
for min(j) in cab2:
I've also tried to write the expression all in one line like this:
def merge_cabs(cab1, cab2):
for min(i) in cabs1 and min(j) in cabs2:
and to loop through a copy of the original lists rather than looping through the lists themselves, because searching through the site, I've found that it can sometimes be difficult to remove elements from a list you're looping through. I've also tried to protect the expressions after the "for" statements inside various configurations of parentheses. If someone sees where the problem(s) lies, it would really be great if you could point it out, or if you have any other observations that could help me better construct this function, I would really appreciate those too.
Here's a very simple-minded solution to this that uses only very basic Python operations:
def merge_cabs(cab1, cab2):
len1 = len(cab1)
len2 = len(cab2)
i = 0
j = 0
newcab = []
while i < len1 and j < len2:
v1 = cab1[i]
v2 = cab2[j]
if v1 <= v2:
newcab.append(v1)
i += 1
else:
newcab.append(v2)
j += 1
while i < len1:
newcab.append(cab1[i])
i += 1
while j < len2:
newcab.append(cab2[j])
j += 1
return newcab
Things to keep in mind:
You should not have any nested loops. Merging two sorted lists is typically used to implement a merge sort, and the merge step should be linear. I.e., the algorithm should be O(n).
You need to walk both lists together, choosing the smallest value at east step, and advancing only the list that contains the smallest value. When one of the lists is consumed, the remaining elements from the unconsumed list are simply appended in order.
You should not be calling min or max etc. in your loop, since that will effectively introduce a nested loop, turning the merge into an O(n**2) algorithm, which ignores the fact that the lists are known to be sorted.
Similarly, you should not be calling any external sort function to do the merge, since that will result in an O(n*log(n)) merge (or worse, depending on the sort algorithm), and again ignores the fact that the lists are known to be sorted.
Firstly, there's a function in the (standard library) heapq module for doing exactly this, heapq.merge; if this is a real problem (rather than an exercise), you want to use that one instead.
If this is an exercise, there are a couple of points:
You'll need to use a while loop rather than a for loop:
while cab1 or cab2:
This will keep repeating the body while there are any items in either of your source lists.
You probably shouldn't delete items from the source lists; that's a relatively expensive operation. In addition, on the balance having a merge_lists function destroy its arguments would be unexpected.
Within the loop you'll refer to cab1[i1] and cab2[i2] (and, in the condition, to i1 < len(cab1)).
(By the time I typed out the explanation, Tom Karzes typed out the corresponding code in another answer...)

"in list" vs. "manual searching" in list

My code in Python reads a next element in list and checks whether it already appeared in the list before. If yes, then it moves a left bound of the list behind the previous appearance (the rest of the code does not matter):
while k<len(lst):
if lst[k] in lst[a:k]: #a is a left bound
i = lst.index(lst[k],a) #could be done more effeciently with exception handling
a = i+1
k += 1
I tried to rewrite this without using high-level tricks (in/index):
while k<len(lst):
for i in range(a,k+1):
if lst[i] == lst[k]:
break
if i != k: #different indices, same values
a = i+1
k += 1
This appears to be cca 3.5 times slower than the code #1. But I do not think the code #2 does something highly inefficient, if I understand the "in" command correctly.
go through all elements in list
compare to the searched element
if they are equal, stop and return True
if at end of the list, return False
(and the function index probably works in the same way, you only have to remember also the index).
My guess is that the Python interpreter interprets the "in" as a low-level version of the for-cycle in code #2. But in the code #2, it has to interpret my comparison every time I increase the value of i, which makes the code run slowly overall. Am I right about this?
By the way, the list is an ordered list of non-repeating numbers (does not have to be, so no suggestions of binary search), which results in a worst-case complexity for this algorithm, n^2/2.

Insertion sort not working - list index out of range

Trying to create insertion sort but receive an error...
Don't really know why it is happening. It always tends to miss 37 aswell
numbers = [45,56,37,79,46,18,90,81,50]
def insertionSort(items):
Tsorted = []
Tsorted.append(items[0])
items.remove(items[0])
for i in range(0,len(items)):
print (Tsorted)
if items[i] > Tsorted[len(Tsorted)-1]:
Tsorted.append(items[i])
else:
Tsorted[len(Tsorted)-2] = items[i]
items.remove(items[i])
insertionSort(numbers)
Error:
if items[i] > Tsorted[len(Tsorted)-1]:
IndexError: list index out of range
First thing: you are removing items from the Array that you are iterating inside the loop here: items.remove(items[i]). This is generally not a good idea.
Second: This algorithm doesn't implement insertion sort, even if you fix the deletion issue. You should review the algorithm, e.g. here Insertion sort in Wikipedia. Thre is another loop required to find the right insertion place.
Third: in the else case, you are overwriting instead of inserting values.
You are removing elements from items over the course of the loop; thus, i may become a value that was a valid index in the original items, but is no longer in the shortened one.
If you need to remove elements from items, it looks like you should wait until the loop is finished.
That's because you're calling tems.remove(). Your code fails when i=4 and items=[37, 46, 90, 50].
So they already has no an element with index 4 but with 3 since indexing starts with 0.
range(0,len(items) will only be calculated the first time your code hits your for-loop, at which state len(list) = 8. Which means that you wlil iterate
for i in [0,1,2,3,4,5,6,7]
#Do stuff...
But at the same time you remove items from your list in each loop. So when hitting i = 4, you have iterated your loop 4 times and the length of you item-list is only 4, which means that items[4]
no longer exists.

Python Lists Append True Boolean

The following [incomplete] code is designed to take in an n to x number, of length x-n, and return the value of the next pandigital number. The code identifies which number between n and x is missing from the number passed in as an argument to the function, and returns (for the time being, until the function is further developed), two lists, the original number itself with its individual digits as members of a list, and a list, with the numbers n to x as members, with those numbers which are present in the original number of length x-n being replaced by the Boolean value True.
def nextPandigital(n,lower,upper):
digits = []
requiredDigits = []
#Completed loop
for digit in str(n):
digits.append(int(digit))
#Completed loop
for num in range(lower,upper+1):
requiredDigits.append(num)
for number in requiredDigits:
if str(number) in str(digits):
x = requiredDigits.index(number)
#requiredDigits[x] = 'True'
requiredDigits[x] = True
return digits, requiredDigits
Although, for the input parameters of nextPandigital(1023456789,0,9) in Enthought Canopy, the second list returned should read [True,True,True,True,True,True,True,True,True,True], the value of the second list returned is, in fact [True,1,True,True,True,True,True,True,True,True], with the 1 from the original requiredDigits list not being replaced by True.
I know that there is no issue with the loop, or the general flow of the code, for when the requiredDigits[x] = True line of code is commented, and the currently commented code is uncommented, the code works as it is intended to, with all digits in requiredDigits being replaced by the String value 'True.'
I have attempted to resolve this issue. However, I am not able to pinpoint its source. I have considered to fact that True == 1 returns True. However, when the value True is replaced by False, in the line requiredDigits[x] = True, the code still works as it is intended to.
Any answer/help/suggestion/advice on this matter would be highly appreciated. Thank you in advance.
The issue is with using index to find where to assign to. Since True is equal to 1, you're mistakenly thinking that the True you entered for 0 is the 1 you want to replace next.
A solution would be to use enumerate to get indexes as you iterate over your list, rather than needing to find them later using index:
for x, number in enumerate(requiredDigits):
if str(number) in str(digits):
requiredDigits[x] = True
A better solution in general would be to use a list comprehension to create the list in one go, rather than starting with numbers and replacing some of them later:
requiredDigits = [True if num in digits else num for num in range(lower,upper+1)]
I'm also getting rid of the unnecessary calls to str in the membership test against digits. You were doing substring testing, rather than testing if the numbers themselves were in the list. That probably wasn't going to cause errors, since the numbers you care about are all one digit long, and the string representation of a list doesn't have any extraneous digits. But in general, its not a good idea to use string operations when you don't need to.
You are checking the same list that you are updating and that is always dangerous.
At the second iteration your variables are:
number=1
requiredDigits = [True, 1, 2, 3, 4, 5, 6, 7, 8, 9]
So when you are doing requiredDigits.index(1) there is a match but it does not reach the 1 since it happens in True==1 so it returns that the index 0
But in general, this implementation is not much pythonic, better check Blckknght's answer

quicksort algorithm in python initial condition

In the following implementation of the quicksort algorithm in Python:
def quicksort(listT):
greater=[]
lower=[]
pivot=[]
if len(listT)<=1:
return listT
else:
pivot=listT[0]
for i in listT:
if i<pivot:
lower.append(i)
elif i>pivot:
greater.append(i)
else:
pivot.append(i)
lower=quicksort(lower)
greater=quicksort(greater)
return lower+pivot+greater
I was wondering what exactly the first condition does in this implementation, for what I see when it divides each part of the list into a greater and lower part, according to the pivot, there would be a moment in which the list has a length lower than 1, but this returned list is not concatenated in any way. Could this condition be changed?
The len(listT)<=1 is needed to terminate the recursion. Quicksort works by dividing the problem into more easily solved subproblems. When the subproblem is an empty list or list of length one, it is already solved (no sorting needed) so the result can be returned directly.
If the initial condition is not stated, then the sort will fail at either
pivot=listT[0] # because the list may be empty and it will reference an invalid index, or
lower=quicksort(lower) # actually it will never end because the stack keeps building on this line.

Categories