Trying to create a double pivot with user inputs, through recursion, although the recursion can be left out, how would a person get the inputs/ints into the list and then let it sort according to the quicksort formula shown.
def swap(lst, i, j):
if min(i,j) >= 0 and max(i,j) < len(lst) and i != j:
lst[i], lst[j] = lst[j], lst[i]
def dpquicksort(lst, left=0, right=None):
if right is None:
right = len(lst) - 1
if right - left >= 1:
p = min(lst[left], lst[right])
q = max(lst[left], lst[right])
l = left + 1
g = right - 1
k = l
while k <= g:
if lst[k] < p:
swap(lst, k, l)
l += 1
elif lst[k] >= q:
while lst[g] > q and k < g:
g -= 1
swap(lst, k, g)
g -= 1
if lst[k] < p:
swap(lst, k, l)
l += 1
k += 1
l -= 1
g += 1
swap(lst, left, l)
swap(lst, right, g)
dpquicksort(lst, left, l-1)
dpquicksort(lst, l+1, g-1)
dpquicksort(lst, g+1, right)
return right
def quickSortHelper(alist, first, last):
if first<last:
splitpoint= partition(alist, first, last)
quickSortHelper(alist, first, splitpoint-1)
quickSortHelper(alist, splitpoint+1, last)
def quicksort(lst):
dpquicksort(lst, 0, len(lst)-1)
print(lst)
lst = [54,26,93,17,77,31,44,55,20]
#lst = int(input("enter integers: "))
quicksort(lst)
lst = [54,6,93,17,7,1,44,55,20]
#lst = [2, 4, 6, 8, 10, 12, 14, 16, 18]
quicksort(lst)
You can utilise map and int built-in,
>>> lst = list(map(int,input("enter integers: ").split()))
enter integers: 2 3 8 1 3 4 1 8 9 2
>>> lst
[2, 3, 8, 1, 3, 4, 1, 8, 9, 2]
Or list comprehension,
[int(num) for num in input("enter integers: ").split()]
It is up to you, put it top or bottom are both fine. Your code become,
def swap(lst, i, j):
if min(i,j) >= 0 and max(i,j) < len(lst) and i != j:
lst[i], lst[j] = lst[j], lst[i]
def dpquicksort(lst, left=0, right=None):
if right is None:
right = len(lst) - 1
if right - left >= 1:
p = min(lst[left], lst[right])
q = max(lst[left], lst[right])
l = left + 1
g = right - 1
k = l
while k <= g:
if lst[k] < p:
swap(lst, k, l)
l += 1
elif lst[k] >= q:
while lst[g] > q and k < g:
g -= 1
swap(lst, k, g)
g -= 1
if lst[k] < p:
swap(lst, k, l)
l += 1
k += 1
l -= 1
g += 1
swap(lst, left, l)
swap(lst, right, g)
dpquicksort(lst, left, l-1)
dpquicksort(lst, l+1, g-1)
dpquicksort(lst, g+1, right)
return right
def quickSortHelper(alist, first, last):
if first<last:
splitpoint= partition(alist, first, last)
quickSortHelper(alist, first, splitpoint-1)
quickSortHelper(alist, splitpoint+1, last)
def quicksort(lst):
dpquicksort(lst, 0, len(lst)-1)
print(lst)
lst = list(map(int,input("enter integers: ").split()))
quicksort(lst)
Related
I wrote the following programming; after running it, the result is not correct.
So what is wrong with my code?
def mergesort(list1):
if len(list1) > 1:
mid = len(list1) // 2
left_list = list1[:mid]
right_list = list1[mid:]
mergesort(left_list)
mergesort(right_list)
i = 0 # left
j = 0 # right
k = 0 # total
while len(list1) > 1 and i < len(left_list) and j < len(right_list):
if left_list[i] < right_list[j]:
list1[k] = left_list[i]
k += 1
i +=1
else:
list1[k] = right_list[i]
k += 1
j += 1
if len(list1) > 1 and i < len(left_list):
list1[k] = left_list[i]
i += 1
k += 1
if len(list1) > 1 and j < len(right_list):
list1[k] = right_list[j]
k += 1
j += 1
return list1
ex = [3, 5, 9, 0, 8, 7]
mergesort(ex)
result: [0, 3, 5, 7, 7, 9]
I found three problems
you didn't get result from mergesort(left_list) mergesort(right_list) (but this could even work because it replaces values in original list)
you used i instead of j in one place
you used if istead of while in two places
def mergesort(list1):
if len(list1) > 1:
mid = len(list1) // 2
left_list = list1[:mid]
right_list = list1[mid:]
left_list = mergesort(left_list) # 1. get result
right_list = mergesort(right_list) # 1. get result
i = 0 # left
j = 0 # right
k = 0 # total
while len(list1) > 1 and i < len(left_list) and j < len(right_list):
if left_list[i] < right_list[j]:
list1[k] = left_list[i]
k += 1
i +=1
else:
list1[k] = right_list[j] # 2. use `j` instead of `i`
k += 1
j += 1
while len(list1) > 1 and i < len(left_list): # 3. use `while`
list1[k] = left_list[i]
i += 1
k += 1
while len(list1) > 1 and j < len(right_list): # 3. use `while`
list1[k] = right_list[j]
k += 1
j += 1
return list1
ex = [3, 5, 9, 0, 8, 7]
print(mergesort(ex))
There are 3 distinct steps in merge sort: the base case, the recursive step, and the merge step. The merge step is often written as a separate function, but here I have incorporated it into the merge_sort function to stay consistent with your code. Your biggest problem was that you did not take you recursively-sorted sub-lists and merge them.
def mergesort(list1):
#base case
if len(list1) <= 1:
return list1
#recursive step
mid = len(list1) // 2
left_list = list1[:mid]
right_list = list1[mid:]
sorted_left = mergesort(left_list)
sorted_right = mergesort(right_list)
#merge step
result = []
while len(sorted_left) > 0 and len(sorted_right) > 0:
if sorted_left[0] <= sorted_right[0]:
result.append(sorted_left.pop(0))
elif sorted_right[0] <= sorted_left[0]:
result.append(sorted_right.pop(0))
else:
result.append(sorted_left.pop(0))
result.append(sorted_right.pop(0))
result = result + sorted_left + sorted_right
return result
print(mergesort([3,5,1,9,7])) #output: [1, 3, 5, 7, 9]
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
def mergeSort(A, l, r):
if l < r:
mid = (l + r) // 2
mergeSort(A, l, mid)
mergeSort(A, mid + 1, r)
merge(A, l, mid, r)
def merge(arr, l, mid, r):
arr1 = []
arr2 = []
for i in range(mid):
arr1.append(arr[i])
for j in range(mid, r):
arr2.append(arr[j])
i = 0
j = 0
k = 0
while (i < len(arr1) and j < len(arr2)):
if arr1[i] < arr2[j]:
arr[k] = arr1[i]
i += 1
else:
arr[k] = arr2[j]
j += 1
k += 1
while i < len(arr1):
arr[k] = arr1[i]
i += 1
k += 1
while j < len(arr2):
arr[k] = arr2[j]
j += 1
k += 1
arr = [2, 9, 7, 6, 1, 8, 4, 3]
mergeSort(arr, 0, 8)
print(arr)
There's a slight mistake somewhere in the code that I'm not able to find
Please try to run this code on your machine with different test cases.
And Let me know what I'm doing wrong here.
I don't know why I'm getting an incorrect answer: [1, 2, 3, 4, 6, 8, 9, 7]
You have problems with indexes. You wrote code in C style.
Just use slices to fix your problem
Change definition (delete for loops for arr1 and arr2) for arr1 and arr2 to :
arr1 = arr[:mid]
arr2 = arr[mid:]
else:
arr[k] = arr2[j]
j += 1
k += 1
Change the place of
k += 1
To this:
else:
arr[k] = arr2[j]
j += 1
k += 1
Or this:
else:
arr[k] = arr2[j]
j += 1
k += 1
There are multiple problems in your code:
you pass the index of the first element to sort and the index once past the last element of the slice, but you wrote the function mergeSort with different semantics as you assume r to be the index to the last element of the slice.
similarly, merge expects the mid argument to be the start of the right half and you pass mid, which would be the index to the last element of the first half in your approach.
in the merge function, arr1 should be initialized with i varying from l to mid, with for i in range(l:mid):
furthermore, k must be initialized to l, not 0.
note that arr1 and arr2 could be initialized from simple slices of arr.
Here is a modified version:
def mergeSort(A, l, r):
if r - l > 1:
mid = (l + r) // 2
mergeSort(A, l, mid)
mergeSort(A, mid, r)
merge(A, l, mid, r)
def merge(arr, l, mid, r):
arr1 = arr[l : mid]
arr2 = arr[mid : r]
i = 0
j = 0
k = l
while (i < len(arr1) and j < len(arr2)):
if arr1[i] <= arr2[j]:
arr[k] = arr1[i]
i += 1
else:
arr[k] = arr2[j]
j += 1
k += 1
while i < len(arr1):
arr[k] = arr1[i]
i += 1
k += 1
while j < len(arr2):
arr[k] = arr2[j]
j += 1
k += 1
arr = [2, 9, 7, 6, 1, 8, 4, 3]
mergeSort(arr, 0, 8)
print(arr)
Output:
[1, 2, 3, 4, 6, 7, 8, 9]
Has someone an idea how to solve the following problem?
Take the numbers 1,...,100000 and permute them in some way. At first you can make a swap of two numbers. Then you have to compute how many rounds it would take to collect numbers in ascending order. You have to collect numbers by every round by going left to right. In how many ways you can swap two numbers at the beginning to collect numbers in ascending order with minimum number of rounds?
For example, if numbers are from one to five and those at the beginning in order 3, 1, 5, 4, 2, then you can collect them in three rounds: On first round you collect 1, 2, on the second round 3, 4 and finally 5. But you can do one swap in three different ways to collect numbers in two rounds, namely
3, 4, 5, 1, 2
3, 1, 4, 5, 2
3, 1, 2, 4, 5
Five number sequence can be solved easily by brute force and I found an algorithm to collect 1000 numbers, but 100000 numbers needs maybe some kind of trick to compute fast how a specific swap at the beginning affects how many rounds it takes to collect numbers.
Another example:
Take 10 numbers in order [6, 1, 4, 10, 7, 2, 3, 9, 5, 8]. You can swap 4 and 9 to collect numbers in three rounds. But my code returns that there are 3 ways to make a swap. Where is my mistake?
from bisect import bisect_left, bisect_right
from functools import cmp_to_key
def longest_subsequence(seq, mode='strictly', order='increasing',
key=None, index=False):
bisect = bisect_left if mode.startswith('strict') else bisect_right
# compute keys for comparison just once
rank = seq if key is None else map(key, seq)
if order == 'decreasing':
rank = map(cmp_to_key(lambda x,y: 1 if x<y else 0 if x==y else -1), rank)
rank = list(rank)
if not rank: return []
lastoflength = [0] # end position of subsequence with given length
predecessor = [None] # penultimate element of l.i.s. ending at given position
for i in range(1, len(seq)):
# seq[i] can extend a subsequence that ends with a lesser (or equal) element
j = bisect([rank[k] for k in lastoflength], rank[i])
# update existing subsequence of length j or extend the longest
try: lastoflength[j] = i
except: lastoflength.append(i)
# remember element before seq[i] in the subsequence
predecessor.append(lastoflength[j-1] if j > 0 else None)
# trace indices [p^n(i), ..., p(p(i)), p(i), i], where n=len(lastoflength)-1
def trace(i):
if i is not None:
yield from trace(predecessor[i])
yield i
indices = trace(lastoflength[-1])
return list(indices) if index else [seq[i] for i in indices]
def computerounds(lines):
roundnumber = 1
for i in range(len(lines)-1):
if lines[i] > lines[i + 1]:
roundnumber += 1
return roundnumber
if __name__ == '__main__':
lines = [[3,1,5,4,2],[6, 1, 4, 10, 7, 2, 3, 9, 5, 8]]
case = 1
ways_to_change = len(longest_subsequence(lines[case], mode='strictly', order='decreasing',
key=None, index=False))
print(len(lines[case]), computerounds(lines[case]), ways_to_change)
# Should return 10 3 1
Effort 1:
I guess the hardest part is to find a permutation that guarantees you collect the numbers with minimum number of moves. I also heard that Dilworth's theorem tells me that the minimal decomposition into ascending subsequences is equal to the size of the maximal descending subsequence. https://artofproblemsolving.com/community/c163h1906044_an_algorithm_to_collect_numbers_in_ascending_order
Effort 2:
I tried to run the code by jferard and solve the problem for the case junar9.in found in https://www.ohjelmointiputka.net/tiedostot/junar.zip. The file contains fir the number of numbers in the first line and then the rest of the lines gives the numbers as in original order. It looks it takes too much memory. The output was in Linux Mint:
(base) jaakko#jaakko-Aspire-E1-572:~/.config/spyder-py3$ python3 temp.py
Killed
Here is the code from temp.py
# -*- coding: utf-8 -*-
"""
Spyder Editor
This is a temporary script file.
"""
import os.path
import requests
import zipfile
import warnings
def invert(L):
M = [None] + [0 for _ in range(len(L))]
for i, k in enumerate(L):
M[k] = i
return M
def perform_data(read_data):
s = ""
for i in range(len(read_data)):
if read_data[i].isnumeric():
s += read_data[i]
else:
s += " "
s = s[:-1]
s = s.split(" ")
tmp = []
for i in range(1, len(s)):
if s[i] != '':
tmp.append(int(s[i]))
return tmp
def download_zipfile(url):
if not os.path.isfile('/tmp/junar.zip'):
with open('/tmp/junar.zip', 'wb') as out:
out.write(requests.get(url).content)
def read_zipfile_item(filename):
with zipfile.ZipFile('/tmp/junar.zip') as zip_file:
with zip_file.open(filename) as f:
return f.read().decode('utf8')
def generate_original_rounds(A):
B =[0]*(len(A)-1)
print(A)
roundno = 1
for i in range(1,len(A)):
if A.index(i) < A.index(i+1):
B[i-1] = roundno
else:
roundno += 1
B[i-1] = roundno
print(roundno)
return B
def classify_moves(L):
M = invert(L)
N = len(L)
good_moves, bad_moves = [None], [None]
for k in range(1, N+1):
good_move, bad_move = find_moves(k, L, M, N)
good_moves.append(good_move)
bad_moves.append(bad_move)
return good_moves, bad_moves
def find_moves(k, L, M, N):
def in_range(a, b):
return set(L[j] for j in range(a, b))
good_move = set()
bad_move = set()
if k == 1:
if M[k+1] < M[k]:
good_move |= in_range(0, M[k+1]+1)
else: # M[k] < M[k+1]
bad_move |= in_range(M[k+1], N)
elif k == N:
if M[k] < M[k-1]:
good_move |= in_range(M[k-1], N)
else: # M[k-1] < M[k]
bad_move |= in_range(0, M[k-1]+1)
elif M[k-1] < M[k+1]:
if M[k] < M[k-1]:
good_move |= in_range(M[k-1], M[k+1])
elif M[k+1] < M[k]:
good_move |= in_range(M[k-1]+1, M[k+1]+1)
if M[k-1] < M[k]:
bad_move |= in_range(0, M[k-1]+1)
if M[k] < M[k+1]:
bad_move |= in_range(M[k+1], N)
else: # M[k+1] < M[k-1]
if M[k+1] < M[k] < M[k-1]:
good_move |= in_range(0, M[k+1]+1) | in_range(M[k-1], N)
elif M[k] < M[k+1]:
bad_move |= in_range(M[k+1], M[k-1])
else: # M[k-1] < M[k]:
bad_move |= in_range(M[k+1]+1, M[k-1]+1)
return good_move, bad_move
def collate_moves_aux(L):
good_moves, bad_moves = classify_moves(L)
N = len(L)
swaps_by_removed = {}
for i in range(1, N+1):
for j in range(i+1, N+1):
removed = 0
if j in good_moves[i]:
if i in good_moves[j]:
removed = 2
elif i not in bad_moves[j]:
removed = 1
elif j not in bad_moves[i] and i in good_moves[j]:
removed = 1
if abs(i-j) <= 1: # don't count twice
removed -= 1
if removed > 0:
swaps_by_removed.setdefault(removed, []).append((i,j))
return swaps_by_removed
def collate_moves(L):
swaps_by_removed = collate_moves_aux(L)
if __name__ == '__main__':
# Testing
url = 'https://www.ohjelmointiputka.net/tiedostot/junar.zip'
download_zipfile(url=url)
rawdata = read_zipfile_item('junar9.in')
data = perform_data(rawdata)
numbers = data
A = collate_moves(numbers)
print(A)
Idea 1: Is it helpful to compute permutation inversions somehow, http://mathworld.wolfram.com/PermutationInversion.html ? There are some algorithms to compute all permutation inversions in https://www.geeksforgeeks.org/counting-inversions/ but does this helps solve the problem? I think it is somehow related to compute the permutation inversions of the form (n,n+1).
Effort 3:
I tried to apply the idea from jferard's answer. I think it computest wrong answer how many rounds it takes to collect numbers [6, 1, 4, 10, 7, 2, 3, 9, 5, 8]. It returns 4 but it takes five rounds, first 1, 2, 3, second 4, 5, third 6, 7, 8, fourth 9, and fifth 10.
def compute_original_rounds(M):
c = 1
for i in range(2, len(M)):
if M[i] < M[i-1]:
c += 1
return c
if __name__ == '__main__':
lines = [[3,1,5,4,2],[6, 1, 4, 10, 7, 2, 3, 9, 5, 8]]
verygoods = 0
lista = lines[1]
best = 0
drops = [0,0,0]
for k in range(2,len(lista)):
a = lista.index(k-1)<lista.index(k)
b = lista.index(k)<lista.index(k+1)
c = lista.index(k-1)<lista.index(k+1)
if a and b:
print("Zero inversions")
drops[0] += 1
if (not a and c) or (c and not b) or (b and not c) or (a and not c):
print("One inversion")
best = max(best,1)
drops[1] += 1
if not b and not a:
print("Two inversions")
best = max(best,2)
drops[2] += 1
ways = drops[2]
if ways == 0:
ways = drops[1]
if ways == 0:
ways = drops[0]
original_rounds = compute_original_rounds(lista)
print(original_rounds)
print(len(lista),original_rounds - best, ways)
I don't see how the longest decreasing subsequence will give you the number of swaps. According to Dilworth's theorem, the longest antichain (subsequence of decreasing numbers) will give you the width of your list, that is the minimum number of chains (sequence of increasing numbers) you can have in partition of the list.
Note that Dilworth's theorem might not be applicable here because the chains (sequences of numbers in your case) should be ordered and the numbers have to be consecutives ([6, 1, 4, 10, 7, 2, 3, 9, 5, 8] is a counter-example: 3 Dilworth's chains but 5 rounds).
Here's an attempt. The solution is complicated and I hope that more straightforward answer exists, but I didn't find it. I cannot say for sure that it is bug free.
Compute the number of rounds
To compute the number of rounds in O(n), let's follow this method:
Start with 1, 2, 3, ... until you find a k having idx(k+1) < idx(k) where idx is the index in the original list (let's call this an inversion).
The first round is finished, and the second starts with k+1, k+2, ... until you find a l having idx(l+1) < idx(l),
and so on until the list is exhausted.
Hence the formula: number of rounds = 1 + |{k in L | pos(k+1)<pos(k)}|. Example with 3,1,5,4,2: idx(3)<idx(2) and idx(5)<idx(4), thus the number of rounds is 3.
In Python:
def invert(L):
M = [None] + [0 for _ in range(len(L))]
for i, k in enumerate(L):
M[k] = i
return M
def rounds(M):
c = 1
for i in range(2, len(M)):
if M[i] < M[i-1]:
c += 1
return c
>>> rounds(invert([3, 1, 5, 4, 2]))
3
>>> rounds(invert([6, 1, 4, 10, 7, 2, 3, 9, 5, 8]))
5
Good and bad moves
That was the easy part. Now focus on a given k in L. You have six possibilities:
... k ... k-1 ... k+1 ... : 1 inversion
... k-1 ... k ... k+1 ... : 0 inversion
... k-1 ... k+1 ... k ... : 1 inversion
... k ... k+1 ... k-1 ... : 1 inversion
... k+1 ... k ... k-1 ... : 2 inversions
... k+1 ... k-1 ... k ... : 1 inversion
We call a "good move" a move from a situation with 1 inversion to a situation with 0 inversion, or from 2 inversions to 1 inversion. Conversely, a "bad move" is a move from a situation with 0 inversion to 1 inversion or 1 inversion to 2 inversions. When performing a swap, we wan't to avoid bad moves and do good moves. The best we can do is to do two good moves at once, reducing the number of rounds by 2.
First, we will compute, for every k, the good and the bad moves. We have to deal with edges cases (k == 1 or k == N), and with the two main possibilities (pos(k-1) < pos(k+1) and pos(k+1) < pos(k-1)). The swaps between k and k-1 or k+1 should be considered too. That gives the cumbrersome piece of code below:
def classify_moves(L):
M = invert(L)
N = len(L)
good_moves, bad_moves = [None], [None]
for k in range(1, N+1):
good_move, bad_move = find_moves(k, L, M, N)
good_moves.append(good_move)
bad_moves.append(bad_move)
return good_moves, bad_moves
def find_moves(k, L, M, N):
def in_range(a, b):
return set(L[j] for j in range(a, b))
good_move = set()
bad_move = set()
if k == 1:
if M[k+1] < M[k]:
good_move |= in_range(0, M[k+1]+1)
else: # M[k] < M[k+1]
bad_move |= in_range(M[k+1], N)
elif k == N:
if M[k] < M[k-1]:
good_move |= in_range(M[k-1], N)
else: # M[k-1] < M[k]
bad_move |= in_range(0, M[k-1]+1)
elif M[k-1] < M[k+1]:
if M[k] < M[k-1]:
good_move |= in_range(M[k-1], M[k+1])
elif M[k+1] < M[k]:
good_move |= in_range(M[k-1]+1, M[k+1]+1)
if M[k-1] < M[k]:
bad_move |= in_range(0, M[k-1]+1)
if M[k] < M[k+1]:
bad_move |= in_range(M[k+1], N)
else: # M[k+1] < M[k-1]
if M[k+1] < M[k] < M[k-1]:
good_move |= in_range(0, M[k+1]+1) | in_range(M[k-1], N)
elif M[k] < M[k+1]:
bad_move |= in_range(M[k+1], M[k-1])
else: # M[k-1] < M[k]:
bad_move |= in_range(M[k+1]+1, M[k-1]+1)
return good_move, bad_move
>>> classify_moves([3, 1, 5, 4, 2])
([None, set(), set(), set(), {1, 5}, {2, 4}], [None, {2}, {1}, {4}, {3}, set()])
That means that, for instance, from 4 point of view, a swap with 1 or 5 are good, and a swap with 3 would be bad.
Choosing the swaps
Now, we have to collate all those good and bad moves into a list of acceptable swaps. the idea is simple: for every couple (i,j), if i is a good move from j and j a good move from i, then we can remove two rounds. If i is a good move from j and i is not a bad move from j, then we can remove one round. There is again some subtle tricks: 1) we have a list of swaps removing 1 round, but we throw away those swaps as soon as we find a swap removing 2 rounds (the best we can do). 2) when k is a good move from k+1 and k+1 a good move from k, we don't remove two rounds but only one (the good move was counted twice by the classify_moves function).
def collate_moves_aux(L):
good_moves, bad_moves = classify_moves(L)
N = len(L)
swaps_by_removed = {}
for i in range(1, N+1):
for j in range(i+1, N+1):
removed = 0
if j in good_moves[i]:
if i in good_moves[j]:
removed = 2
elif i not in bad_moves[j]:
removed = 1
elif j not in bad_moves[i] and i in good_moves[j]:
removed = 1
if abs(i-j) <= 1: # don't count twice
removed -= 1
if removed > 0:
swaps_by_removed.setdefault(removed, []).append((i,j))
return swaps_by_removed
def collate_moves(L):
swaps_by_removed = collate_moves_aux(L)
return max(swaps_by_removed.items(), key=lambda i: i[0])
>>> collate_moves_aux([3, 1, 5, 4, 2])
{1: [(1, 4), (2, 5), (4, 5)]}
>>> collate_moves([3, 1, 5, 4, 2])
(1, [(1, 4), (2, 5), (4, 5)])
And:
>>> collate_moves_aux([6, 1, 4, 10, 7, 2, 3, 9, 5, 8])
{1: [(3, 8), (5, 10), (8, 9), (9, 10)], 2: [(4, 9)]}
>>> collate_moves([6, 1, 4, 10, 7, 2, 3, 9, 5, 8])
(2, [(4, 9)])
The complexity of the algorithm is O(N^2) amortized: invert is O(N), classify_moves is O(N^2) because find_moves is O(N) (build sets having a cardinal < N) and collate_moves is O(N^2) (amortized).
Hope someone's produce a simple version of this!!!
I would consider a structure like the following. Dashes show us where there is a switch between output sequence rounds. Candidates that need replacement are in the square brackets; these are the indexes immediately before and after the round switch. In parentheses, we have ranges of increasing index sequences that neighbour the candidates.
i: 1 2 3 4 5 6 7 8 9 10
A: 2 1 6 7 8 9 3 4 5 10
indexed, ordered output:
2 1 7 8 9 3 4 5 6 10
- - (round switches)
[2,1](7..8)[9,3](4..10)
Now we insert the candidates, as well as the lower and upper bounds of each range into a binary search tree (or just a sorted array on which we can binary search), where each node also points to their position in the indexed, sorted output list. For each candidate index, we would like to test other indexes in the tree that fit. Once found, we can perform a linear search from the index to gather more possibilities. Notice that candidates that warrant replacing are the ones that if removed would offer an increasing sequence between their neighbours in the input.
2: find x ≤ 1: result 1
1: find 2 ≤ x ≤ 7: result 3
(linear search is stopped by 9 and 4)
9: invalid candidate
3: invalid candidate
Swaps are therefore indexes (2,1) or (1,3).
Example 1 from the question:
i: 1 2 3 4 5
A: 3 1 5 4 2
indexed, ordered output:
2 5 1 4 3
- - (round switches)
(2)[5,1][4,3]
candidates:
5: invalid candidate
1: invalid candidate
4: find 1 ≤ x ≤ 3: results 2, 3
3: find x ≥ 4: result 5
swaps: (4,2) (4,3) (3,5)
Example 2 from the question:
i: 1 2 3 4 5 6 7 8 9 10
A: 6 1 4 10 7 2 3 9 5 8
indexed, ordered output:
2 6 7 3 9 1 5 10 8 4
- - - - (round switches)
(2..6)[7,3][9,1](5)[10,8,4]
candidates:
7: invalid
3: find 7 ≤ x ≤ 9: result 8 (9 wouldn't reduce rounds)
9: invalid
1: invalid
10: find 5 ≤ x ≤ 8: results 7, 8
8: invalid
4: find x ≥ 8: results 8, 9 (10 wouldn't reduce rounds)
original rounds:
(1 2 3)(4 5)(6 7 8)(9)(10)
swaps:
(3,8) -> (1 2 3 4 5)(6 7 8)(9 10)
(10,7) -> (1 2 3)(4 5)(6 7 8 9)(10)
(10,8) -> (1 2 3)(4 5)(6 7 8 9)(10)
(4,8) -> (1 2 3)(4 5)(6 7 8)(9 10)
(4,9) -> (1 2 3)(4 5)(6 7 8)(9 10)
I followed the algorithm in the book for this problem. when I print result it is not correct. the algorithm is exactly as in the book
my code
import math
def quickSelect(A, k):
m = A[math.floor(len(A)/2)]
L = [i for i in A if i < m]
E = [i for i in A if i == m]
G = [i for i in A if i > m]
print(L)
print(E)
print(G)
if k <= len(L):
return quickSelect(L, k)
elif k <= (len(E) + len(G)):
return m
else:
return quickSelect(G, k- len(L) - len(E))
result = quickSelect([7, 14, 10, 12, 2, 11, 29, 3, 4], 4)
print(result)
These statements:
L = [i for i in A if i < m] # less than m
E = [i for i in A if i == m] # equal to m
G = [i for i in A if i > m] # greater than m
partition the array into three ranges:
| L1 L2 L3 L4 | E1 | G1 G2 G3
| | |
0 | |
len(L) |
len(L) + len(E)
Your condition for the second range,
elif k <= (len(L) + len(G)):
return m
is wrong. It should be:
elif k <= (len(L) + len(E)):
return m
Node: Instead of using floating-point math, you can just calculate m with Python's integer division: m = A[len(A) // 2]
I was reading about merge sort(In place) in my algorithm book (Intro to algorithms 3rd edition Cormen), and I decided to implement it in Python. The problem is that I can't find what I am doing wrong... I saw some code in C++, but even with that I can't fix it.
Here is my code:
def MERGE(A,start,mid,end):
L=[0]*(mid - start + 1)
for i in range(len(L) - 1):
L[i] = A[start+i]
L[len(L) - 1] = 100000 # centinel, (fix)
R=[0]*(end - mid + 2)
for j in range(len(R) - 1):
R[j] = A[mid+j]
R[len(R) - 1] = 100000
i = 0
j = 0
k = start
for l in range(k,end):
if(L[i] < R[j]):
A[l] = L[i]
i = i + 1
else:
A[k] = R[j]
j = j + 1
def mergeSort(A,p,r):
if p < r:
mid = int((p+r)/2)
mergeSort(A,p,mid)
mergeSort(A,mid+1,r)
MERGE(A,p,mid,r)
A = [20, 30, 15, 21, 42, 45, 31, 0, 9]
mergeSort(A,0,len(A)]
When I run the code I have some index problems:
File "myrealmerge.py", line 9, in MERGE
R[j] = A[mid+j]
IndexError: list index out of range
I know that this my be a "dumb question" and that there is some related post, but I tried the suggestions in there and It does not work for me...
Can anyone help me? T
Thanks!
This code works fine:
def MERGE(A,start,mid,end):
L = A[start:mid]
R = A[mid:end]
i = 0
j = 0
k = start
for l in range(k,end):
if j >= len(R) or (i < len(L) and L[i] < R[j]):
A[l] = L[i]
i = i + 1
else:
A[l] = R[j]
j = j + 1
def mergeSort(A,p,r):
if r - p > 1:
mid = int((p+r)/2)
mergeSort(A,p,mid)
mergeSort(A,mid,r)
MERGE(A,p,mid,r)
A = [20, 30, 21, 15, 42, 45, 31, 0, 9]
mergeSort(A,0,len(A))
print A
I tried to minimize the change from your code.
Good luck!
(Added)
You can check the dividing process by using this code.
def MERGE(A,start,mid,end):
# Do nothing
pass
def mergeSort(A,p,r):
if r - p > 1:
mid = int((p+r)/2)
print A[p:mid],A[mid:r]
mergeSort(A,p,mid)
mergeSort(A,mid,r)
MERGE(A,p,mid,r)
A = [20, 30, 21, 15, 42, 45, 31, 0, 9]
mergeSort(A,0,len(A))
The result is as follows:
[20, 30, 21, 15] [42, 45, 31, 0, 9]
[20, 30] [21, 15]
[20] [30]
[21] [15]
[42, 45] [31, 0, 9]
[42] [45]
[31] [0, 9]
[0] [9]
This is what we want. However, 'mid+1' makes invalid result. Here is the test code:
def MERGE(A,start,mid,end):
# Do nothing
pass
def mergeSort(A,p,r):
if r - p > 1:
mid = int((p+r)/2)
print A[p:mid],A[mid+1:r] # Changed
mergeSort(A,p,mid)
mergeSort(A,mid+1,r) # Changed
MERGE(A,p,mid,r)
A = [20, 30, 21, 15, 42, 45, 31, 0, 9]
mergeSort(A,0,len(A))
result:
[20, 30, 21, 15] [45, 31, 0, 9]
[20, 30] [15]
[20] []
[45, 31] [9]
[45] []
(Added)
Here is a code using 'mid+1':
# New indexing function that includes the right index.
def get_partial_list(origin_list, left_index, right_index): # Added
return origin_list[left_index:right_index+1]
def MERGE(A,start,mid,end):
L = get_partial_list(A,start,mid)
R = get_partial_list(A,mid+1,end)
i = 0
j = 0
k = start
for l in range(k,end+1): # changed
if j >= len(R) or (i < len(L) and L[i] < R[j]):
A[l] = L[i]
i = i + 1
else:
A[l] = R[j]
j = j + 1
def mergeSort(A,p,r):
if r - p > 0: # changed
mid = int((p+r)/2)
mergeSort(A,p,mid)
mergeSort(A,mid+1,r) # changed
MERGE(A,p,mid,r)
A = [20, 30, 21, 15, 42, 45, 31, 0, 9]
mergeSort(A,0,len(A)-1) # changed
print A
I've added new indexing function. Is this the code you expected?
The solutions provided by lancif and krishna Prasad dont not have space complexity O(1).
Here is algorithm in place merge sorting for Py3 with space complexity O(1).
The main function sort_imerge uses 2 auxiliary functions wmerge and wsort.
This solution based on C code that discussed on:
other SO topic and on good C implementation
Also you can find full Python code with doctests here
def sort_imerge(Seq, l=0, u=None):
""" Merge sorting, mutable input.
Input sequence changed in place.
Time: O(n * log n)
log n -- levels
n -- elements on each level must be merged
Space (additional): O(1)
input changed in place
Returns None
"""
u = len(Seq) if u is None else u
if u - l > 1:
m = l + (u - l) // 2
w = l + u - m
wsort(Seq, l, m, w)
while w - l > 2:
n = w
w = l + (n - l + 1) // 2
wsort(Seq, w, n, l)
wmerge(Seq, l, l + n - w, n, u, w)
n = w
while n > l: # fallback to insert sort
for m in range(n, u):
if Seq[m-1] > Seq[m]:
Seq[m-1], Seq[m] = Seq[m], Seq[m-1]
n -= 1
def wmerge(Seq, i, m, j, n, w):
"""Merge subarrays [i, m) and [j, n) into work area w.
All indexes point into Seq.
The space after w must be enough to fit both subarrays.
"""
while i < m and j < n:
if Seq[i] < Seq[j]:
Seq[i], Seq[w] = Seq[w], Seq[i]
i += 1
else:
Seq[j], Seq[w] = Seq[w], Seq[j]
j += 1
w += 1
while i < m:
Seq[i], Seq[w] = Seq[w], Seq[i]
i += 1
w += 1
while j < n:
Seq[j], Seq[w] = Seq[w], Seq[j]
j += 1
w += 1
def wsort(Seq, l, u, w):
"""Sort subarray [l, u) and put reuslt into work area w.
All indexes point into Seq.
"""
if u - l > 1:
m = l + (u - l) // 2
sort_imerge(Seq, l, m)
sort_imerge(Seq, m, u)
wmerge(Seq, l, m, m, u, w)
else:
while l < u:
Seq[l], Seq[w] = Seq[w], Seq[l]
l +=1
w +=1
Recursively split the array into left and right part and then merge it as per your requirements i.e ASC or DESC, Check the below code:
def merge_sort(a):
if len(a) <= 1:
return a
left = [];
right = [];
mid = len(a)/2
left = a[0:mid]
right = a[mid:(len(a))]
print left
print right
#exit()
left = merge_sort(left)
right = merge_sort(right)
return merge(left, right)
def merge(left, right):
result = []
while len(left) > 0 and len(right) > 0:
lv = left[0]
rv = right[0]
if lv <= rv:
result.append(lv)
left.pop(0)
else:
result.append(rv)
right.pop(0)
while len(left) > 0:
result.append(left.pop(0))
while len(right) > 0:
result.append(right.pop(0))
return result
A = [20, 30, 21, 15, 42, 45, 31, 0, 9]
print A
print merge_sort(A)
For understanding I have printed the intermediate partitioned array have a look into the output:
[20, 30, 21, 15, 42, 45, 31, 0, 9]
[20, 30, 21, 15]
[42, 45, 31, 0, 9]
[20, 30]
[21, 15]
[20]
[30]
[21]
[15]
[42, 45]
[31, 0, 9]
[42]
[45]
[31]
[0, 9]
[0]
[9]
[0, 9, 15, 20, 21, 30, 31, 42, 45]
Hope this will help you to understand the logic.