How do I derive this part of the algorithm - python

I'm a beginner at programming Python and came across this program.
This algorithm is used to reverse a list:
mylist = [1,2,3,4]
reverse = mylist[:]
for i in range(len(reverse)//2):
reverse[i], reverse[len(reverse) -i -1] = reverse[len(reverse) -i -1], reverse[i]
This algorithm is based on the logic that the swapping process will happen only till the len/2 element for lists with even lengths or the len/2 element in case of lists with a odd length because if the swapping process occurred till the last element, the list would remain the same as it was in the beginning.
I understood what the below part does, but how do I derive it, please explain the logic:
reverse[len(reverse) -i -1]

len(reverse) returns the number of elements the list has. To actually use it as an index you need to subtract 1 from it as indices start from 0. Next, we also subtract i from it as we are moving i positions away from both ends of the list and swapping them.
So if i=1 reverse[i] points to 2 while reverse[len(reverse)-i-1 points to 3.

You can just print the contents of these variables:
lst = [1, 2, 3, 4, 5, 6]
for i in range(len(lst)//2):
print(i, len(lst) - i - 1)
This will print
0 5
1 4
2 3
Therefore,
for the index of the first element (i = 0), len(lst) - i - 1 will return the index of the last element (i = 5).
for the index of the second element (i = 1), len(lst) - i - 1 will return the index of the second to last element (i = 4).
And so on.

Related

Python find index of last element smaller than number

I have a list of numbers from 0 to 3 and I want to remove every number that is smaller than 2 xor is not connected to the last 3 in the list. It is also going to be done about 200 Million times so it should preferably perform well. For example, I could have a list like that:
listIwantToCheck = [3, 0, 1, 2, 0, 2, 3, 2, 2, 3, 2, 0, 2, 1]
listIWantToGet = [2, 3, 2, 2, 3]
I already have the index of the last 3 so what I would do is:
listIWantToGet = listIWantToCheck[??? : indexOfLastThree + 1]
??? being 4 in this instance. It is the index with the mentioned conditions.
So How do I get the index of the last number smaller than 2?
Nailed it, the index i want is
index = ([0]+[i for i, e in enumerate(listIWantToCheck[:indexOfLastThree]) if e < 2])[-1] + 1
List comprehension is truly beautiful.
I enumerated through the slice and created a list of all indices, which point to a number smaller than 2 and took the last. The 0 in front is added to circumvent an index error, which would occur if there are no elements smaller than 2.
So if i get this right you want to get the index of the last number smaller than 2 that comes before the last 3.
My approach would be to take the part of the list from index 0 to the index of the last 3 and then reverse the list and check if the number is smaller than 2.
If you however want to get the last 2 of the entire list just reverse it and loop through it the same way
for i in listIWanttoCheck[0:indexOfLastThree].reverse():
if i <2:
return listIWanttoCheck.index(i)
Correct me if I missunderstood your problem

Non-tail recursion within a for loop

Given an array of numbers, find the length of the longest increasing subsequence in the array. The subsequence does not necessarily have to be contiguous.
For example, given the array [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15], the longest increasing subsequence has length 6: it is 0, 2, 6, 9, 11, 15.
One of the solutions to the above problem uses non-tail recursion within a for loop, and I am having trouble making sense of it. I don't understand when the code after the recursive call in the for loop is executed, and I can't visualize the entire execution process of the whole solution.
def longest_increasing_subsequence(arr):
if not arr:
return 0
if len(arr) == 1:
return 1
max_ending_here = 0
for i in range(len(arr)):
ending_at_i = longest_increasing_subsequence(arr[:i])
if arr[-1] > arr[i - 1] and ending_at_i + 1 > max_ending_here:
max_ending_here = ending_at_i + 1
return max_ending_here
The description of the solution is as follows:
Assume that we already have a function that gives us the length of the longest increasing subsequence. Then we’ll try to feed some part of our input array back to it and try to extend the result. Our base cases are: the empty list, returning 0, and an array with one element, returning 1.
Then,
For every index i up until the second to last element, calculate longest_increasing_subsequence up to there.
We can only extend the result with the last element if our last element is greater than arr[i] (since otherwise, it’s not increasing).
Keep track of the largest result.
Source: https://www.dailycodingproblem.com/blog/longest-increasing-subsequence/
**EDITS**:
What I mean by I don't understand when the code after the recursive call in the for loop is executed. Here is my understanding:
Some code calls lis([0, 8, 4, 12, 2]).
arr = [0, 8, 4, 12, 2] doesn't meet either of the two base cases.
The for loop makes the first call when i = 0 in the line ending_at_i = lis([]). This is the first base case, so it returns 0. I can't understand why control doesn't return to the for loop so that ending_at_i is set to 0, and the if condition is executed (because it surely isn't checked else [][-1] would throw an error), after which we can move on to the for loop making the second call when i = 1, third call when i = 2 which would branch into two calls, and so on.
Here's how this function works. Fist, it handles the degenerate cases where the list length is 0 or 1.
It then looks for the solution when the list length is >= 2. There are two possibilities for the longest sequence: (1) It may contain the last number in the list, or (2) It may not contain the last number in the list.
For case (1), if the last number in the list is in the longest sequence, then the number before it in the longest sequence must be one of the earlier numbers. Suppose the number before it in the sequence is at position x. Then the longest sequence is the longest sequence taken from the numbers in the list up to and including x, plus the last number in the list. So it recurses on all of the possible positions of x, which are 0 through the list length minus 2. It iterates i over range(len(arr)), which is 0 through len(arr)-1). But it then uses i as the upper bound in the slice, so the last element in the slice corresponds to indices -1 through len(arr)-2. In the case of -1, this is an empty slice, which handles the case where all values in the list before the last are >= the last element.
This handles case (1). For case (2), we just need to find the largest sequence from the sublist that excludes the last element. However, this check is missing from the posted code, which is why the wrong answer is given for a list like [1, 2, 3, 0]:
>>> longest_increasing_subsequence([1, 2, 3, 0])
0
>>>
Obviously the correct answer in this case is 3, not 0. This is fairly easy to fix, but somehow was left out of the posted version.
Also, as others have pointed out, creating a new slice each time it recurses is unnecessary and inefficient. All that's needed is to pass the length of the sublist to achieve the same result.
Here is a (hopefully good enough) explanation:
ending_at_i = the length of the LIS when you clip arr at the i-th index (that is, considering elements arr[0], arr[1], ..., arr[i-1].
if arr[-1] > arr[i - 1] and ending_at_i + 1 > max_ending_here
if arr[-1] > arr[i - 1] = if the last element of arr is greater than the last element of the part of arr correponding to ending_at_i
if ending_at_i + 1 > max_ending_here = if appending the last element of arr to the LIS found during computing ending_at_i is larger than the current best LIS
The recursive step is then:
Let an oracle tell you the length of the LIS in arr[:i] (= arr[0], arr[1], ..., arr[i-1])
realize that, if the last element of arr, that is, arr[-1], is larger than the last element of arr[:i], then whatever the LIS inside arr[:i] was, if you take it and append arr[-1], it will still be an LIS, except that it will be one element larger
Check whether arr[-1] is actually larger than arr[i-1], (= arr[:i][-1])
Check whether appending arr[-1] to the LIS of arr[:i] creates the new optimal solution
Repeat 1., 2., 3. for i in range(len(arr)).
The result will be the knowledge of the length of the LIS inside arr.
All that being said, since the recursive substep of this algorithm runs in O(n), there are very few worse feasible solutions to the problem.
You tagged dynamic programming, however, this is precisely the anti-example of such. Dynamic programming lets you reuse the solutions to subproblems, which is precisely what this algorithm doesn't do, hence wasting time. Check out a DP solution instead.

Trying to make a "folded list" in python?

I need to make a "folded in half list" where I multiply the first and last term, second and second to last term, third and third to last term, etc.
I know how to make a list and how to print specific values from the list, but don't understand how to do math like this within the list, without simply typing each number and multiplying them.
So far all I have is a list (called a) with six terms, and though I know how to multiply the whole times an integer, I don't know how to make this "folded" thing. Will I need to end up making a loop of some sort? If so, how?
Edit: I should have specified that I need to make it via a function. Someone helped me make one via a method that worked great, but when I realized it needed to be a function I tried it again and it won't work. See the code below.
a = [10, 14, 21, 25 ,52, 55]
print('starting list:', a)
Finding the middle of the list
len(a)//2
Using (what I think is) a function to make a new list of first + last term, etc.
Term i is the term counted from the start of the list, term -(i+1) is the term counted from the bottom of the list
print(sum((a[i]) + (a[-(i + 1) ])) for i in range( len(a) // 2)
A code that works but isn't a function since it has brackets
foldedlist=[ a[i] + a[-(i + 1) ] for i in range( len(a) // 2)]
print('folded list:', foldedlist)
Take your list, here:
l = [1, 2, 3, 4, 5, 6]
Find the mid-way point (where the fold happens - you'll need to think about how this works for an odd-numbered length list)
l_mid = len(l) / 2
Make two sublists up to, and then from the mid-point
l1 = l[:l_mid]
l2 = l[l_mid:]
Create a container for the output and then iterate over both lists, with the latter reversed and append the product to the container.
output = []
for v1, v2 in zip(l1, reversed(l2)):
output.append(v1 * v2)
As we know, indexes in list can be positive or negative. Index -1 refers to last item, index -2 refers to second last item, etc. Hence, we must have:
L[0]*L[-1]+
L[1]*L[-2]+
L[2]*L[-3]+
...+
L[mid]*L[-(mid+1)]
The general formula is: L[i]*L[-(i+1)]. So, we can use the following code:
list = [1, 2, 3, 4, 5, 6]
newList=[ list[i] * list[-(i + 1) ] for i in range( len(list) // 2)]
print(newList)
len(list) // 2 is the index of middle of list.
Output: [6, 10, 12]
Try like this:
myList = [Yourlist]
newList = []
a = myList[:len(myList)//2]
b = myList[len(myList)//2:]
b.reverse()
for x,y in zip(a,b):
newList.append(x*y)

Index out of range confusion

So i am new to programming, and i am having trouble with index out of range errors. Quick example:
I have a list, lst = (5,7,8,9,10).
I want to remove every even number, and every number to the right of an even number.
I would approach this problem by getting the index of every even number, 'i' , and removing lst[i] and lst [i+1]. That will not work when the last number is even because there is no lst [i+1] after the last element in the list.
I have run into this issue on several basic problems i have been working on. My approach to solving this is probably wrong, so i would like to know:
How can i/Can i solve the problem this way, whether it is efficient or not?
What would be the most efficient way to solve this problem?
Welcome to the club! Programming is a lot of fun and something you can always improve upon with incremental progress. I'm going to try to be exhaustive with my answer.
With lists (also known as arrays) remember that a list and its indexes are zero-based. What this means is that an array's indexes start at the number 0 (not number 1 like you would do in normal counting).
arr = [5, 7, 8, 9, 10]
# If you want to access the first element of the array
# then you would use the 0 index. If you want the Second
# element you use index 1.
print(arr[0]) # prints 5 or the 1st element
print(arr[1]) # prints 7 or the 2nd element
I would not use your stand looping technique like for or while in this case because you are removing elements are you are going for the array. If you delete the item as you are looping you are changing the length of the array.
Instead, you could create a new array from looping and only adding or appending odd values to this new array.
arr = [5, 7, 8, 9, 10]
new_arr = []
for idx, val in enumerate(arr):
if idx % 2 == 1:
new_arr.append(val)
return new_arr # yields [7,9] or this process creates a new array of odd elements
In addition, remember when you are using [i+1] while you are indexing through loop in makes sense to stop the loop an element early to avoid an out of index range error.
Do this (no error)
for idx in range(len(arr)-1):
# pseudocode
print(arr[i] + arr[i+1])
instead of this (out of index error). The reason being is that on the last element if you try to add 1 to last index and then access a value that does not exist then an error will be returned:
for idx in range(len(arr)):
# pseudocode
print(arr[i] + arr[i+1])
arr = [5, 7, 8, 9, 10]
# if you try to access arr[5]
# you will get an error because the index
# and element do not exist
# the last element of arr is arr[4] or arr[-1]
arr[5] # yields an out of index error
There are many Pythonic (almost like a colloqial phrase specific to python) ways to accomplish your goal that are more efficient below.
You can use slicing, spacing and the del (delete statment) to remove even number elements
>>> arr = [5, 7, 8, 9, 10]
>>> del arr[::2] # delete even numbers # if you wanted to delete odd numbers del arr[1::2]
>>> arr
[7, 9]
Or a list comprehension to create a new list while looping through some conditional to filter the even numbers out:
new_arr = [elem for idx, elem in enumerate(arr) if idx % 2 == 0]
The % operator is used to see if there is a remainder from division. So if idx is 10. Then 10 % 2 == 0 is true because 2 is able to divide into 10 five times and the remainder is 0. Therefore, the element is even. If you were checking for odd the condition would be:
idx % 2 == 1
You can find further explanation of these Python methods from this great Stack Overflow post here
One issue you may run into is your list indexes shifting on you during removal. One way around this is to sort the indexes to be removed in descending order and remove them first.
Here is an example of how you could accomplish what you are looking for:
myList = [5, 7, 8, 9, 10]
# use list comprehension to get indexes of even numbers into a list.
# num % 2 uses the modulus operator to find numbers divisible by 2
# with a remainder of 0.
even_number_indexes = [idx for idx, num in enumerate(myList) if num % 2 == 0]
# even_number_indexes: [2, 4]
# sort our new list descending
even_number_indexes.reverse()
# even_number_indexes: [4, 2]
# iterate over even_number_indexes and delete index and index + 1
# from myList by specifying a range [index:index + 2]
for index in even_number_indexes:
del myList[index:index + 2]
print(myList)
output: [5, 7]
You can check if i+1 is greater than (Edit: or equal to) the length of the list, and if it is, not execute the code.
You can also handle this in a try/except block.
As to the efficiency of this method of solving, seems fine to me. One gotcha in this approach is that people try to iterate over the list while modifying it, which can lead to unknown errors. If you're using the remove() function, you probably want to do it with a copy of the list.

why the swapping does not take place when indices are 0 and 1?

I have an unordered array consisting of consecutive integers [1, 2, 3, ..., n] without any duplicates. It is allowed to swap any two elements. I need to find the minimum number of swaps required to sort the array in ascending order.
Starting from the first element of the list, I try to put them in their right position (for example if the first element is 7 it should be in the 6th position of the list). To go one by one in the list, I make a copy, and do the swapping in the second list.
a = [4,3,1,2]
b = a[:]
swap = 0
for p in a:
if (p!= b[p-1]):
b[p-1], b[b.index(p)] = b[b.index(p)], b[p-1]
swap+=1
print(swap)
this code works, except for the case that I have to swap two elements in the list whose position is either 0 or 1, in this case . which I don't understand why?? since I'm not exceeding the limit.
Can anyone please explain to me why this happens?
For example, if I print p, two indices where swapping happens, updated list of b and updated number of swaps:
p = 4
idx1= 3 idx2= 0
b= [2, 3, 1, 4]
swap = 1
p = 3
idx1= 2 idx2= 1
b= [2, 1, 3, 4]
swap = 2
p = 1
idx1= 0 idx2= 1
b= [2, 1, 3, 4]
swap = 3
p = 2
idx1= 1 idx2= 0
b= [1, 2, 3, 4]
swap = 4
In this case, you can see that for p = 1, when indices are 0 and 1, the swapping is not taking place.
I changed the order of b[p-1], b[b.index(p)] and I don't have the same problem anymore, but I don't understand the reason.
I have encountered the same problem before, and stucked for a while. The reason to cause this is the order of multiple assignment.
b[p-1], b[b.index(p)] = b[b.index(p)], b[p-1]
Actually multiple assignment is not exactly assign at the same time. pack and unpack mechanism behind this. so it will change b[p-1] first, and b.index(p) in b[b.index(p)] will be find a new index which is p-1 when the case p=1 idx1=0 idx2=1
if you change the assignment order, it will work fine.
b[b.index(p)], b[p - 1] = b[p - 1], b[b.index(p)]
or calculate idx first:
idx1, idx2 = p - 1, b.index(p)
b[idx1], b[idx2] = b[idx2], b[idx1]
I recommend the second version, because first version will do index twice. cost twice time than second version.
you can refer to my related question here: The mechanism behind mutiple assignment in Python
by the way, I think your algorithm is inefficient here, to decrease swap time, but use O(n) index operation, and also copy a array here. I think you can use the same idea, just swap in orignal array.

Categories

Resources