Translating pseudocode into Python - python

This is the pseudocode I was given:
COMMENT: define a function sort1
INPUT: a list of numbers my list
print the initial list
loop over all positions i in the list; starting with the second element (index 1)
COMMENT: at this point the elements from 0 to i-1 in this list are sorted
loop backward over those positions j in the list lying to the left of i; starting at position i-1 continue this loop as long as the value at j+1 is less than the value at j
swap the values at positions j and j+1
print the current list
And this is the python code I came up with:
#define a function sort1
my_list=range(1,40)
print
print my_list
num_comparisons=0
num_swaps=0
for pos in range (0,len(my_list)-1):
for i in range(pos+1,len(my_list)): # starting at position i-1 continue this loop as long
# as the value at j+1 is less than the value at j
num_comparisons+=1
if my_list[i]<my_list[pos]:
num_swaps+=1
[my_list[i],my_list[pos]]=[my_list[pos],my_list[i]]
print my_list
print
print num_comparisons, num_swaps
I'm not sure I did it correctly though.

As I said in a comment, I think you effectively do have the j (as well as the i) the pseudocode COMMENT talks about. However, in your code, i is the variable pos, which would make the j what is named i in your code.
To see if your code works, you need to initially have an unsorted list—not the my_list=range(1,40) in your code (which is [1, 2, 3, ... 38, 39] and already in numerical order).
One thing you didn't do is define a sort1() function.
What is below is essentially your code, but I renamed the two variables to match the pseudocode COMMENT, and put (most of it) in a function definition where it's supposed to be.
Beyond that, I had to declare the variables num_comparisons and num_swaps (which aren't mentioned in the psuedocode) as global so they could be accessed outside of the function—otherwise they would have been local variables by default and only accessible within the function.
def sort1(items):
""" Sort given list of items in-place. """
# allow access to these variables outside of function
global num_comparisons
global num_swaps
# initialize global variables
num_comparisons = 0
num_swaps = 0
# starting at position i-1 continue this loop as long
# as the value at j+1 is less than the value at j
for i in range(0, len(items)-1):
for j in range(i+1, len(items)):
num_comparisons += 1
if items[j] < items[i]:
num_swaps += 1
[items[j], items[i]] = [items[i], items[j]]
my_list = [6, 3, 7, 2, 9, 4, 5]
print 'my_list before sort:'
print my_list
sort1(my_list)
print 'my_list after sort:'
print my_list
print
print 'num_comparisons:', num_comparisons, ', num_swaps:', num_swaps
Output:
my_list before sort:
[6, 3, 7, 2, 9, 4, 5]
my_list after sort:
[2, 3, 4, 5, 6, 7, 9]
num_comparisons: 21 , num_swaps: 10

Related

Merging two sorted arrays in python

I am trying to merge two sorted arrays recursively, and I can merge the first few numbers until one pointer exits the array. There seems to be some problem with the base case not getting executed. I have tried to print the new_arr with the pointers for each recursive call to debug but cannot seem to find a solution. Here is my code:
new_arr= []
i= 0
j=0
def merge(arr1, arr2, i, j):
#base case
##when arr1 pointer exits
print(i,j, new_arr)
if(i>len(arr1)-1):
new_arr.append(arr2[j:])
return new_arr
##when arr2 pointer exits
if (j > len(arr2)-1):
new_arr.append(arr1[i:])
return new_arr
if(arr1[i]<arr2[j]):
new_arr.append(arr1[i])
i+=1
merge(arr1, arr2, i, j)
elif(arr1[i]>=arr2[j]):
new_arr.append(arr2[j])
j+=1
merge(arr1, arr2, i, j)
sortedarr = merge([1,9], [3,7,11,14,18,99], i, j)
print(sortedarr)
and here goes my output:
0 0 []
1 0 [1]
1 1 [1, 3]
1 2 [1, 3, 7]
2 2 [1, 3, 7, 9]
None
These are the issues:
new_arr.append(arr2[j:]) should be new_arr.extend(arr2[j:]). append is for appending one item to the list, while extend concatenates a second list to the first. The same change needs to happen in the second case.
As you count on getting the mutated list as a returned value, you should not discard the list that is returned by the recursive call. You should return it back to the caller, until the first caller gets it.
It is a bad idea to have new_arr a global value. If the main program would call the function a second time for some other input, new_arr will still have its previous values, polluting the result of the next call.
Although the first two fixes will make your function work (for a single test), the last issue would best be fixed by using a different pattern:
Let the recursive call return the list that merges the values that still needed to be analysed, i.e. from i and j onwards. The caller is then responsible of prepending its own value to that returned (partial) list. This way there is no more need of a global variable:
def merge(arr1, arr2, i, j):
if i >= len(arr1):
return arr2[j:]
if j >= len(arr2):
return arr1[i:]
if arr1[i] < arr2[j]:
return [arr1[i]] + merge(arr1, arr2, i + 1, j)
else:
return [arr2[j]] + merge(arr1, arr2, i, j + 1)
sortedarr = merge([1,9], [3,7,11,14,18,99], i, j)
print(sortedarr)
Note that Python already has a built-in function that knows how to merge sorted arrays, heapq.merge.
list(heapq.merge((1, 3, 5, 7), (2, 4, 6, 8)))
[1, 2, 3, 4, 5, 6, 7, 8]

Multiplying only odd indexes in a for loop

Multiplying only odd indexes with for loop
Why its not working?
myList = [1, 2, 3, 4, 5]
index = 0
for num in myList:
if index % 2 != 0:
num *= 2
index += 1
print(myList) # here the output still the same list
I want the output to be [1, 4, 3, 8, 5]
Edit: I see that you have edited your question, where you fixed the first issue of checking if the items in the list are odd or not. You are however still iterating over the list using for num in myList, which under the hood creates an Iterator which moves over your list. This means that whatever you do with num, you are only modifying num and not myList[index]. In order to modify the list directly, you will need to reference myList[index]. I strongly recommend you look into using enumerate, see my original answer for how to apply it to your use-case.
Your problem is that you are checking if the value inside myList is even or odd instead of its index.
Also, modifying num within the loop does not modify the value in your original list (this can easily be noticed since in the "modified" list the odd values are not multiplied).
Using the range(len()) idiom to loop over your list would yield the following code:
myList = [1, 2, 3, 4, 5]
for idx in range(len(myList)):
if idx % 2 != 0:
myList[idx] *= 2
print(myList)
You could further shorten the loop/assignment by using enumerate and list comprehension:
myList = [1, 2, 3, 4, 5]
myList = [num * 2 if idx % 2 != 0 else num for idx, num in enumerate(myList)]
print(myList)
Your way isn't working because you are not assigning 'num' back into the list.
newList = [v*2 if idx % 2 != 0 else v for idx, v in enumerate(myList)]
There are two issues in the code -
You need to check if the index is odd or even, and not the element.
Modifying num won't modify the corresponding element in the list as it does not reference the element in the list.
myList = [1, 2, 3, 4, 5]
for idx in range(len(myList)):
if idx % 2 != 0:
myList[idx]*=2

For Loop, why index out of range?

Write a function named same_values() that takes two lists of numbers of equal size as parameters.
The function should return a list of the indices where the values were equal in lst1 and lst2.
For example, the following code should return
[0, 2, 3]
same_values([5, 1, -10, 3, 3], [5, 10, -10, 3, 5])
My original attempt:
def same_values(lst1, lst2):
new_list=[]
for i in lst1:
for j in lst2:
if lst1[i] == lst2[j]:
new_list.append(lst1[i])
return new_list
I looked up the solution which is:
def same_values(lst1, lst2):
new_lst = []
for index in range(len(lst1)):
if lst1[index] == lst2[index]:
new_lst.append(index)
return new_lst
yet I wonder why my original attempt is not valid (it says list index out of range which I don't get in this case). Thanks everyone for helping me out!
Just try:
lst1 = [5, 1, -10, 3, 3]
for i in lst1:
print(i)
You are actually iterating over the values in the list, not the indexes.
In the below condition i is considered as array value. so i would be 5,1,-1, which are invalid indexes
def same_values(lst1, lst2):
new_list=[]
for i in lst1:
for j in lst2:
if lst1[i] == lst2[j]:
new_list.append(lst1[i])
return new_list
You have 2 major issues (i.e. go against the problem statement) with your logic:
You are comparing every value in list 1 to every value in list 2. So, if you the following lists [10,1,10,1,10] and [1,10,1,10,1] your code would identify all 5 position as positive hits
Instead of recording the index or position where you get the a positive hit, you are recording the corresponding value from list 1. In your example, instead of [0,2,3] you would get [5,-10,3]
That being said, have you actually tried running your code? It would be very helpful to you if you actually debugged your code and stepped through each operation.

Learning python... stumped. Where is the loop in this function?

I do not understand how the result is 10...
Specifically where in the function does it create the loop that adds 1, 2, 3 and 4?
I am also new to Stackoverflow, so if there is a relative article that I overlooked then please do refer me.
def func(x):
res=0
for i in range(x):
res += i
return res
print(func(5))
def func(x): # defines the function name and input parameter
res=0 # created a variable that is right now set to 0
for i in range(x): # this has two parts, for-loop, and a range function both explained below
res += i # this adds 1 to the variable 'res' each time the for-loop loops
return res # end of the function, passes the variables value back to the caller
print(func(5)) # call the function while passing 5 as an argument
This is how a for loop works,
it will loop over each element you provide it.
so,
myPets = ['dog', 'cat', 'rabbit'] # create a list of pets
for pet in myPets:
print pet # print each pet
When this runs, you get
dog
cat
rabbit
Now the range function, creates a sequence of x numbers ranging from 0 to x-1 so,
range(5)
is equivalent to:
[0,1,2,3,4]
Keep in mind, it starts at 0 and ends at x-1
we could also do
range(3, 6)
which would be equivalent to:
[3,4,5]
note that in python2 range actually returns the list where as in python3 range is a separate sequence type. For the purposes of a for loop, they do the same thing.
As mentioned in comments, you need to know what does the range function to understand that loop.
range(x) function creates an array which contains from 0 to x-1. So range(5) create the array [0, 1, 2, 3, 4]. So the loop:
for i in range(5)
it's equivalent to:
for i in [0, 1, 2, 3, 4]
for i in range(x):
This is the loop you are looking for. It iterates over every element in range(x).
When you have range(5), you are telling python to generate 5 integers, which are up to but not including 5. So they are 0, 1, 2, 3, 4.
The += operator adds right operand to the left operand and assign the result to left operand.
So in your case, with the function and the loop iterating from 0 to 4, you are telling python to generate a function called func(x); within this function, generate a range of integers, from 0 up to but not including 5; add whatever i is to res (to add 0, 1, 2, 3, 4 sequentially to res).
Finally it becomes 10.
res += i means res= res+i
so for loop loops as below
res = 0, initially
The supplied list for looping is
[0,1,2,3,4]
so res += i is applied for each element of the list
the variable 'i' is a temporary variable used to loop through the for loop function.
so value of 'i' will be changing every time it loops i.e
i=0
i=1
i=2
i=3
i=4
the value of res keeps on changing as per the for loop
res= 0+0 =0
res= 0+1 =1
res= 1+2 =3
res= 3+3 =6
res= 6+4 =10
Final returned value is 10 as for loop ends at 4 value in the list
From Python.org:
If you do need to iterate over a sequence of numbers, the built-in function range() comes in handy. It generates lists containing arithmetic progressions, e.g.:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Essentially, for i in range(10) is the same as saying for i in [1,2,3,4,5,6,7,8,9,10]
The += operator is the same as saying res = res + value
So, in combination with the range statement, this piece of code is looking at the first element of the list, 0, adding that to zero(your starting element: res = 0), then adding one to that, then adding two to the result of the previous computation (resulting in 3), and so on.

Trying to understand insertion sort algorithm

I'm reading some books on Python, data structures, and analysis and design of algorithms. I want to really understand the in's and out's of coding, and become an efficient programmer. It's difficult to ask the book to clarify, hence my question on stackoverflow. I'm really finding Algorithms and recursion challenging ... I posted some code (insertion sort) below that I'm trying to understand exactly what's happening. I understand, generally, what is supposed to happen, but I'm not really getting the how and why.
From trying to analyze pieces of the code on Python Idle, I know that:
key (holds variables) = 8, 2, 4, 9, 3, 6
and that:
i (holds the length) = 7 ( 1, 2, 3, 4, 5, 6, 7)
I don't know why 1 is used in the first line: range(1, len(mylist)). Any help is appreciated.
mylist = [8, 2, 4, 9, 3, 6]
for j in range(1,len(mylist)):
key = mylist[j]
i = j
while i > 0 and mylist[i-1] > key:
mylist[i] = mylist[i - 1]
i -= 1
mylist[i] = key
Let me try to break this down.
Start by considering a list. It is "almost" sorted. That is, the first few elements are sorted, but the last element is not sorted. So it looks something like this:
[10, 20, 30, 50, 15]
Obviously, the 15 is in the wrong place. So how do we move it?
key = mylist[4]
mylist[4] = mylist[3]
mylist[3] = key
That'll switch around the 15 and the 50 so now the list looks like:
[10, 20, 30, 15, 50]
But we'd like to do this several times in a loop. To do that we can do:
while ???:
key = mylist[i]
mylist[i] = mylist[i-1]
mylist[i-1] = key
i -= 1
That loop will go back one position at a time swapping the two elements. That'll move the out of order position one place back each time. But how do we know when to stop?
Let's look again at our list and the moves we want to make:
[10, 20, 30, 50, 15]
[10, 20, 30, 15, 50]
[10, 20, 15, 30, 50]
[10, 15, 20, 30, 50]
# stop! we are sorted now!
But what is different that last time around? Every time we move the number one place back, it is because the 15 is less then the element on the left, meaning its not sorted. When that is no longer true we should stop moving. But we can easily deal with that:
key = mylist[i]
while key < mylist[i-1]:
mylist[i] = mylist[i-1]
mylist[i-1] = key
i -= 1
Ok, but happens if we now try to sort this list:
[10, 20, 1]
[10, 1, 20]
[1, 10, 20]
# ERROR!!
At this point something bad happens. We try to check whether key < mylist[i-1] but when we've reached the beginning, i = 0, and this checks the end of the list. But we should stop moving to left at this point...
If we reach the beginning of the list, we can't move our pivot/key further so we should stop. We update our while loop to handle that:
key = mylist[i]
while i > 0 and key < mylist[i-1]:
mylist[i] = mylist[i-1]
mylist[i-1] = key
i -= 1
So now we have a technique for sorting an almost sorted list. But how can we use that to sort a whole list? We sort parts of the list at a time.
[8, 2, 4, 9, 3, 6]
First we sort the first 1 elements:
[8, 2, 4, 9, 3, 6]
Then we sort the first 2 elements:
[2, 8, 4, 9, 3, 6]
Then we sort the first 3 elements
[2, 4, 8, 9, 3, 6]
So on and so forth
[2, 4, 8, 9, 3, 6]
[2, 4, 8, 9, 3, 6]
[2, 3, 4, 8, 9, 6]
[2, 3, 4, 6, 8, 9]
But how do we do we do that? With a for loop
for j in range(len(mylist)):
i = j
key = mylist[i]
while i > 0 and key < mylist[i-1]:
mylist[i] = mylist[i-1]
mylist[i-1] = key
i -= 1
But we can skip the first time through, because a list of one element is obviously already sorted.
for j in range(1, len(mylist)):
i = j
key = mylist[i]
while i > 0 and key < mylist[i-1]:
mylist[i] = mylist[i-1]
mylist[i-1] = key
i -= 1
A few minor changes which make no difference brings us back to your original code
for j in range(1, len(mylist)):
key = mylist[j]
i = j
while i > 0 and key < mylist[i-1]:
mylist[i] = mylist[i-1]
i -= 1
mylist[i] = key
The insertion sort algorithm works by trying to build up a sorted list of increasing length at the start of the array. The idea is that if you start off by building a one-element sorted list at the beginning, then a two-element list, then a three-element list, etc., that once you've built up an n-element sorted list, you have sorted the entire array and are done.
For example, given the array
3 1 4
We can split this into a zero-element sorted list and a three-element unsorted list:
| 3 1 4
Now, we add 3 to our sorted list. Since that list is now only one element long, it's automatically sorted:
3 | 1 4
Now, we want to add 1 to our sorted list. If we just add 1 to the end of the list like this:
3 1 | 4
then the sorted list is no longer sorted. To fix this, the inner loop of the insertion sort code works by continuously swapping the 1 with the element before it until it's in the proper position. In our case, we swap the 1 and the 3:
1 3 | 4
and since the 1 is now at the beginning of the array, we don't need to move it any more. This is why the inner loop runs while i > 0; once the index of the new element (i) is at the start of the array, there's nothing before it that could be any bigger.
Finally, we update the array by adding 4 to the sorted list. Since it's in sorted position, we're done:
1 3 4
And our array is now in sorted order.
Now, to your original question: why does the outer loop start at 1? This is a cute optimization trick. The idea is that any one-element array must automatically be sorted. This means that the algorithm can start off by saying that the first element of the array is a one-element sorted list. For example, given the array
2 7 1 8
The insertion sort algorithm could try splitting this array like this, putting an empty sorted list at the front:
| 2 7 1 8
But a marginally faster option is to split the list like this:
2 | 7 1 8
which is guaranteed to be safe because any one-element list is automatically sorted.
This is really an optimization of the algorithm on the part of the authors. The algorithm would work perfectly fine if the outer loop started at zero, but they've just decided to start it at one to avoid an unnecessary loop iteration.
Hope this helps!
Have a look at the while loop. It starts with i having the value of 1, but then i is decreased. So in the last line, the minimum value of i could be 0, which is the first element in the list. If you would start with 0, i would become -1 which is valid in python, but means the last element. Therefore the range has to start with 1.
I would like to mention, that you are asking for insertion sort. I don't thin that your code implements insertion sort. Looks rather like bubble sort or something like that.
The reason is that:
i = j
and that mylist is accessed like:
mylist[i - 1]
Therefor the first value is 0. If the range would have started at 0, it would cause an mylist to be accessed at position -1.
Check out animated InsertionSort HERE
Later on i = j is set, and and myList[i-1] is accessed. So, j must be j >= 1.
Added: setting j = 0 is logicaly wrong because in the loop myList[j-1] is accessed - this is just by doing statical analysis of the code (and knowing i = j). Even if this cannot happen during runtime because of while i > 0, it is at least meaningless. If the expression myList[j-1] appears in the code, then it must surely be j >= 1.
The j-the iteration inserts the j-th element into the sorted elements before j. So it makes no sense to start with j=0. In the case j=1 the sublist below is myList[0:1] which is allways sorted, and the loop inserts myList[1] into the sublist myList[0:2]

Categories