Rearrange numbers in the min and max form - python

Given the following input
[1,2,3,4,5,6,7]
We are suppose to sort the numbers so that they are sorted as following
[7,1,6,2,5,3,4]
So i figured i can basically create a new list and use two pointers one starting at left most index and the other starting at right most index. Since the list is already sorted the right pointer gives me the max value and left gives me the min value. So i basically keep on adding values of max and min and update the left pointer to move right and the right pointer to move left. Following is my solution.
def maxMin(lst):
result = []
left = 0
right = len(lst) - 1
while left < right:
result.append(lst[right])
result.append(lst[left])
left += 1
right -= 1
if len(lst) % 2 != 0:
result.append(lst[left])
return result
While this solution works apparently this uses extra space and there is a possible way to come up with a solution that uses only constant space. I came across this video on youtube but i had no idea how the author came up with the solution. To me understanding the arrival at solution is more important than the actual solution. I would love if someone can shed some light.

The extra space complained of is because you create a second list to hold the result. With a small amount of data that is an entirely reasonable way to go about it. But if your list had thousands of entries instead of a handful, then making a copy might not be feasible. In that case you might need to move the elements of the list in place.
We start by observing that the list is sorted and the elements in the last half of the list get moved to earlier in the list, at even-numbered positions (0, 2, 4). So, first calculate how many elements have to be shifted to earlier in the list. Then, for each of the elements to be shifted, calculate its new position (0, 2, 4) and move it there.
The first element to be shifted is at the end of the list. After it has been moved to an earlier point, the next element to be shifted is now at the end of the list. So the item to be moved is always the last in the list, and that can be extracted using pop().
Like this:
a = [1,2,3,4,5,6,7]
shifts = len(a)//2
for i in range(shifts):
print(f"shifting {a[-1]} to position {i*2}")
a.insert(i*2,a.pop())
print (a)
Output from the last print() call is [7, 1, 6, 2, 5, 3, 4]. The print() call inside the loop is simply there to show you what is happening.
This alternative way of doing it trades execution time for space. Every time you insert an element in a list, other than at the end, all of the subsequent elements have to be moved to the right to make room. That does a lot of processing behind your back, even though it is a single line of code.

There is a sort in place strategy that minimizes memory movements (I believe inserting in a list reallocates it internally). It is based on a naive sort algorithm that swaps the value at each position with the subsequent position having the lowest value. With a small adjustment to the logic, we can make the sort decide that each position will alternatively swap the lowest and highest value depending on whether the position is odd or even.
This sort-in place will perform N swaps and N*(N-1)/2 comparisons so the memory overhead will be minimal and there will be no reallocation.
def updownSort(a):
for i in range(len(a)-1): # for each position except the last one
p=i # identify the swapping position (p)
for j in range(i+1,len(a)): # only look in remaining positions
if bool(i&1) == (a[p]>a[j]): # alternate seeking the largest and smallest value
p = j
a[p],a[i] = a[i],a[p] # make the swap with the index that was found
a = [1,2,3,4,5,6,7]
updownSort(a)
print(a)
# [7, 1, 6, 2, 5, 3, 4]
Note that any other "sort in place" algorithm (e.g. bubble sort) could be adapted in the same fashion as long as the comparisons can take into account the positions being compared in addition to the values.

Related

How to prove the correctness of this solution for Codeforces problem "A. Boredom"?

I am looking at the Codeforces problem A. Boredom:
Given a sequence π‘Ž consisting of 𝑛 integers. The player can make several steps. In a single step he can choose an element of the sequence (let's denote it π‘Žπ‘˜) and delete it, at that all elements equal to π‘Žπ‘˜+1 and π‘Žπ‘˜βˆ’1 must also be deleted from the sequence. That step brings π‘Žπ‘˜ points to the player.
I found the following code submitted by user Leeisateam:
input()
z = [0] * 7**6
for i in map(int, input().split()):
z[i] += i
a = b = 0
for i in z:
a, b = max(a, i + b), a
print(a)
I understand what this code is doing up until the final loop starts. We're creating a list z of the form
[0 X count_in_sequence(0), 1 X count_in_sequence(1), ..., n X count_in_sequence(n)].
After that b is assigned the value of a and a uses that (previous) value in the next iteration. I tried induction, but I still can't understand why this code would always work.
Some observations:
The order of the sequence is of no importance. If you'd shuffle the input order, the output would still be the same. So we might as well think of the input being sorted.
When values are "isolated", i.e. the array no longer has a value that is one less or one more, then we should always take it: there is no "penalty" -- only the chosen value gets removed from the array. If we wouldn't take it, it would still be there after all other possible moves have been performed, and we would still take it at the end of the game then.
If the same value occurs multiple times, and we decide to take one of those, we should certainly also take the other occurrences, as then we arrive in the case of the previous point: there will be no more "collateral damage" (as the "neighboring" values were already removed by the first pick).
If we would have an input with 𝑛 numbers with all unique numbers between 1 and 𝑛, and 𝑛 would be odd, then we maximize by taking 1, 3, 5,... 𝑛-2, 𝑛.
If we would have the same as above, but now with 𝑛 even, we would take 2, 4, 6, ..., 𝑛-2, 𝑛. We could also have considered 1, 3, 5,... 𝑛-3, 𝑛-1, or leave an extra gap somewhere in between, (e.g. 1, 3, 6, 8, 10,... 𝑛-2, 𝑛), but in the end the first variant would have the maximum sum.
When moving through an input from left to right, we only have to consider two scenarios: the first one where the current value minus one was selected, and a second scenario where it was not selected. In the second scenario, we can select the current value, in thirst scenario we cannot, because it was removed by the previous move. If we select the current value using the second scenario, it automatically becomes the first scenario for the next potential move on (value plus one), while the first scenario becomes the second with regards to (value plus one). So we alternative scenarios and at the end pick the best scenario of both.
Now let's look at the code.
The code projects the values to their corresponding index, so that they are automatically visited in ascending order.
Then the variables a and b are introduced. They represent two alternative solutions of picking values before the current value i is taken (or not). The one represented by a is one that might have taken the value i-1, and so if that would be done, the value i would not be available anymore (as deleted by the previous pick). By consequence, we don't consider the a scenario for picking i. On the other hand b represents a variant where we surely did not pick i-1, and so we are sure that after scenario b we can also pick value i. So now after having considered i, we have two scenarios: a (which wasn't extended) and b + i (note also that i has all the duplicates summed up as we know we want to take all or nothing of it).
In the next iteration of the loop, the role of a and b change, since now b has possibly selected that previous i, which now is i-1, and that previous b is now a (it is like a swap).
And so the loop "alternates" through the z array, keeping track of two possible scenarios.
Describing this in words makes it quite verbose, but it does help to step through the code with a piece of paper using some limited inputs.

Having issues implementing recursion in my attempt at a quick sort algorithm in Python

I am trying to write my own quick sort algorithm in Python without looking up how it's done professionally (I will learn more this way). If my idea of how I intend to implement this quick sort seems silly to you, (I am aware that it probably will) please don't give me a completely different way of doing it, unless my method will never succeed or at least not without ridiculous measures, please help me reach a solution with my desired method :)
Currently I have a defined a function "pivot" which will take the input list and output three lists, a list of numbers smaller than the pivot (chosen in this case to be the first number in the list every time), a list of numbers equal to the pivot and a list of numbers greater than the pivot.
My next step was to define a function "q_sort". First this function creates a list called "finalList" and fills it with 0s such that it is the same length as the list being sorted. Next it pivots the list and adds the the numbers equal to the pivot to finalList in what is already their correct position (as there are 0s in place to represent the number of items smaller than it and 0s as place-holders again in place of the items bigger than pivot)
This all works fine.
What doesn't work fine is the next step. I have written what I want to happen next in some poorly thought out psuedo-code below:
numList = [3, 5, 3, 1, 12, 65, 2, 11, 32]
def pivot(aList):
biggerNum =[]
smallerNum = []
equalNum = [aList[0]]
for x in range(1, len(aList)):
if aList[0]<aList[x]:
biggerNum.append(aList[x])
elif aList[0]>aList[x]:
smallerNum.append(aList[x])
elif aList[0] == aList[x]:
equalNum.append(aList[x])
pivoted = [smallerNum, equalNum, biggerNum]
return pivoted
def q_sort(aList):
finalList = []
for x in range(len(aList)):
finalList.append(0)
pivot(aList)
for i in range(len(pivot(aList)[1])):
finalList[len(pivot(aList)[0])+i] = pivot(aList)[1][i]
Pseudo Code:
#if len(smallerNum) != 0:
#q_sort(smallerNum) <--- I want this to add it's pivot to finalList
#if len(biggerNum) != 0:
#q_sort(biggerNum) <--- Again I want this to add it's pivot to finalList
#return finalList <--- Now after all the recursion every number has been pivoted and added
What I intend to happen is that if the list of numbers smaller than the pivot actually has any items in it, it will then q_sort this list. This means it will find a new pivot and add it's value to the right position in finalList. The way I imagine it working is that the function only reaches "return finalList" once every number from "numList" has been put in it's correct position. As the recursive nature of including q_sort within q_sort means after pivoting "smallerNum" (and adding the pivot to finalList) it will have another list to pivot.
The problem is that you're starting over on each call: using the entire list, working from both ends. You need to recur on each partition of the list: the part below the pivot, then the part above the pivot. This is generally done by passing the endpoints of the sub-list, such as ...
def q_sort(aList, low, high):
if low >= high:
return
# find pivot position, "pivot"
...
# arrange list on either side of pivot
...
# recur on each part of list.
q_sort(alist, low_index, pivot-1)
q_sort(alist, pivot+1, high_index)
Is that enough of an outline to get you moving?
If not, try a browser search on "Python quicksort", and you'll find a lot of help, more thorough than we can cover here.

Finding numbers in a list of permutations based on the position of one of the numbers in the list

I've been coding a program that solves a maths problem with specific conditions.
In this maths problem, there are seven slots. At the beginning, the first three slots are occupied by the numbers 1, 2 and 3. There is an empty slot in the middle. The next three are numbers 4, 5 and 6. Seven slots altogether.
The goal of the problem is for the 1 2 and 3 to switch places with the 4 5 and 6. Only one number can be moved at a time. It can move over another number into an empty slot (the zero) or move sideways switching with an empty slot.
Below is a visualisation of the start:
1 2 3 0 4 5 6
The desired outcome is shown below:
4 5 6 0 1 2 3
Keep in mind that it does not have to be in this order, as long as 4 5 6 is on one side and 1 2 3 is on the other with 0 in the middle.
The program that we are creating uses itertools for a list of permutations. Then, based on the position of the zero, finds a permutation that suits the next move.
What I need is for specific combinations to be extracted (output) from this list, based on the position of zeroes within the combinations. Below is the code so far.
import time
import itertools
nonStop = True
answerList = [1, 2, 3, 0, 4, 5, 6]
combinations = itertools.permutations([1, 2, 3, 0, 4, 5, 6])
while nonStop == True:
for value in combinations:
i = 0
print(value)
i += 1
time.sleep(2)
Thanks in advance. Any help would be greatly appreciated!
Here is some high-level advice on your program. I'll assume that on each move the zero will swap places with a number that is one or two slots distance from the zero. I'll also assume that you want to find the smallest number of moves that takes you from the initial list to a "desired outcome" and that the printout is to show all the positions from the starting position to the desired outcome.
If you know graph theory (also called network theory) you could easily solve your problem by performing a breadth-first search from your starting position to any desired position. The nodes in your graph are the 5,040 possible permutations, and two nodes have an edge between them if you can go from one position to the other in one move.
If you don't know graph theory, you can use this following approach. You can use two overall data structures: a queue (such as collections.deque) and a dictionary. Put the initial position into the queue. Also put it as a key in the dictionary with the value None.
Then run a loop. In each run through the loop, remove one position from the queue. There will be at most four possible moves from this position: swap the zero with the item 2 left, 1 left, 1 right, or 2 right of the zero. (If the zero is at or near an end, the possible moves will be fewer.) For each of those moves, if the resulting position is not already in the dictionary, add it to the queue and the dictionary. The dictionary entry's value is the position you just took from the queue. If the resulting position is already in the dictionary, do nothing.
Now check if the resulting position is a "desired result". If not, continue the loop. If it is, use the dictionary to save all the moves from your desired result back to the initial position. Then print those positions in the desired order, and you are done--break the loop.
Note three things about this approach. First, if any sequence of moves reaches a desired result, this approach will print one of the shortest sequences. Second, not all permutations of the original position are generated. Permutations are generated one at a time until a desired result is reached--there is no need for more. Third, no printing is done until all the moves are done and the good moves have been picked out. This is because most moves will not prove to be useful, so we wait until we know which the useful ones are.
If you want more information, show some more work of your own. But before that, tell me if I understood the rules of your problem correctly.

Returning index of elements from subarray of a nested list

I'm having a lot of issues trying create the following function...horizontal(m) that takes a 2D list (representing a matrix) as input, searches for 4 elements that are in arithmetic progression and returns the locations (indices) of these elements. (An aside: it's also suppose to search for 4 elements in arithmetic progression vertically and diagonally, but I'm just working on the horizontal search now.) I've tried a bunch of different codes, but nothing seems to work out and we haven't worked with nested lists at all in my class at school. Please advise!
def horizontal(m):
diff = m[0][1] - m[0][0]
index=[]
for i in range(len(m)-1):
for j in range(len(m[i])):
if m[i][j+1] - m[i][j] == diff:
index.append(m[i][j])
return index
>>>horizontal([[1, 2, 3, 4],
[2, 5, 6, 9]])
>>>[1] #but I would like it to return: [[0,0],[0,1],[0,2],[0,3]]
Also, I'm aware that this currently doesn't limit the function's search to 4 elements...I just wanted to figure out how to properly return the indices before continuing. Thank you!
Use index.append([i, j]) instead of your current append line.
This essentially is appending a list which contains the value of i as its first element and the value of j as its second elemen, which is what you want.
Currently, your code is appending the value of the element at m[i][j], which isn't what you want.
EDIT (in response to comments):
Fixed the error :-) I had accidentally forgot(ten) to put the brackets around the square brackets.
You need to return the index list after the for loops (reindent your return line so that it's one indentation level deeper than the declaration of the function (it should be aligned with the index = [] line)).

Python lists - codes & algorithem

I need some help with python, a new program language to me.
So, lets say that I have this list:
list= [3, 1, 4, 9, 8, 2]
And I would like to sort it, but without using the built-in function "sort", otherwise where's all the fun and the studying in here? I want to code as simple and as basic as I can, even if it means to work a bit harder. Therefore, if you want to help me and to offer me some of ideas and code, please, try to keep them very "basic".
Anyway, back to my problem: In order to sort this list, I've decided to compare every time a number from the list to the last number. First, I'll check 3 and 2. If 3 is smaller than 2 (and it's false, wrong), then do nothing.
Next - check if 1 is smaller than 2 (and it's true) - then change the index place of this number with the first element.
On the next run, it will check again if the number is smaller or not from the last number in the list. But this time, if the number is smaller, it will change the place with the second number (and on the third run with the third number, if it's smaller, of course).
and so on and so on.
In the end, the ()function will return the sorted list.
Hop you've understand it.
So I want to use a ()recursive function to make the task bit interesting, but still basic.
Therefore, I thought about this code:
def func(list):
if not list:
for i in range(len(list)):
if list[-1] > lst[i]:
#have no idea what to write here in order to change the locations
i = i + 1
#return func(lst[i+1:])?
return list
2 questions:
1. How can I change the locations? Using pop/remove and then insert?
2. I don't know where to put the recursive part and if I've wrote it good (I think I didn't). the recursive part is the second "#", the first "return".
What do you think? How can I improve this code? What's wrong?
Thanks a lot!
Oh man, sorting. That's one of the most popular problems in programming with many, many solutions that differ a little in every language. Anyway, the most straight-forward algorithm is I guess the bubble sort. However, it's not very effective, so it's mostly used for educational purposes. If you want to try something more efficient and common go for the quick sort. I believe it's the most popular sorting algorithm. In python however, the default algorithm is a bit different - read here. And like I've said, there are many, many more sorting algorithms around the web.
Now, to answer your specific questions: in python replacing an item in a list is as simple as
list[-1]=list[i]
or
tmp=list[-1]
list[-1]=list[i]
list[i]=tmp
As to recursion - I don't think it's a good idea to use it, a simple while/for loop is better here.
maybe you can try a quicksort this way :
def quicksort(array, up, down):
# start sorting in your array from down to up :
# is array[up] < array[down] ? if yes switch
# do it until up <= down
# call recursively quicksort
# with the array, middle, up
# with the array, down, middle
# where middle is the value found when the first sort ended
you can check this link : Quicksort on Wikipedia
It is nearly the same logic.
Hope it will help !
The easiest way to swap the two list elements is by using β€œparallel assignment”:
list[-1], list[i] = list[i], list[-1]
It doesn't really make sense to use recursion for this algorithm. If you call func(lst[i+1:]), that makes a copy of those elements of the list, and the recursive call operates on the copy, and then the copy is discarded. You could make func take two arguments: the list and i+1.
But your code is still broken. The not list test is incorrect, and the i = i + 1 is incorrect. What you are describing sounds a variation of selection sort where you're doing a bunch of extra swapping.
Here's how a selection sort normally works.
Find the smallest of all elements and swap it into index 0.
Find the smallest of all remaining elements (all indexes greater than 0) and swap it into index 1.
Find the smallest of all remaining elements (all indexes greater than 1) and swap it into index 2.
And so on.
To simplify, the algorithm is this: find the smallest of all remaining (unsorted) elements, and append it to the list of sorted elements. Repeat until there are no remaining unsorted elements.
We can write it in Python like this:
def func(elements):
for firstUnsortedIndex in range(len(elements)):
# elements[0:firstUnsortedIndex] are sorted
# elements[firstUnsortedIndex:] are not sorted
bestIndex = firstUnsortedIndex
for candidateIndex in range(bestIndex + 1, len(elements)):
if elements[candidateIndex] < elements[bestIndex]:
bestIndex = candidateIndex
# Now bestIndex is the index of the smallest unsorted element
elements[firstUnsortedIndex], elements[bestIndex] = elements[bestIndex], elements[firstUnsortedIndex]
# Now elements[0:firstUnsortedIndex+1] are sorted, so it's safe to increment firstUnsortedIndex
# Now all elements are sorted.
Test:
>>> testList = [3, 1, 4, 9, 8, 2]
>>> func(testList)
>>> testList
[1, 2, 3, 4, 8, 9]
If you really want to structure this so that recursion makes sense, here's how. Find the smallest element of the list. Then call func recursively, passing all the remaining elements. (Thus each recursive call passes one less element, eventually passing zero elements.) Then prepend that smallest element onto the list returned by the recursive call. Here's the code:
def func(elements):
if len(elements) == 0:
return elements
bestIndex = 0
for candidateIndex in range(1, len(elements)):
if elements[candidateIndex] < elements[bestIndex]:
bestIndex = candidateIndex
return [elements[bestIndex]] + func(elements[0:bestIndex] + elements[bestIndex + 1:])

Categories