Я пытаюсь изменить этот код quicksort для работы с точкой опоры, которая вместо этого принимает "медиану из трех".
def quickSort(L, ascending = True):
quicksorthelp(L, 0, len(L), ascending)
def quicksorthelp(L, low, high, ascending = True):
result = 0
if low < high:
pivot_location, result = Partition(L, low, high, ascending)
result += quicksorthelp(L, low, pivot_location, ascending)
result += quicksorthelp(L, pivot_location + 1, high, ascending)
return result
def Partition(L, low, high, ascending = True):
print('Quicksort, Parameter L:')
print(L)
result = 0
pivot, pidx = median_of_three(L, low, high)
L[low], L[pidx] = L[pidx], L[low]
i = low + 1
for j in range(low+1, high, 1):
result += 1
if (ascending and L[j] < pivot) or (not ascending and L[j] > pivot):
L[i], L[j] = L[j], L[i]
i += 1
L[low], L[i-1] = L[i-1], L[low]
return i - 1, result
liste1 = list([3.14159, 1./127, 2.718, 1.618, -23., 3.14159])
quickSort(liste1, False) # descending order
print('sorted:')
print(liste1)
Но я не совсем уверен, как это сделать. Медиана должна быть медианой первого, среднего и последнего элемента списка. Если в списке есть четное число элементов, средний элемент становится последним элементом первой половины.
Здесь моя средняя функция:
def median_of_three(L, low, high):
mid = (low+high-1)//2
a = L[low]
b = L[mid]
c = L[high-1]
if a <= b <= c:
return b, mid
if c <= b <= a:
return b, mid
if a <= c <= b:
return c, high-1
if b <= c <= a:
return c, high-1
return a, low
Давайте сначала реализуем медианную из трех для трех чисел, поэтому независимая функция. Мы можем это сделать, сортируя список из трех элементов, а затем возвращаем второй элемент, например:
def median_of_three(a, b, c):
return sorted([a, b, c])[1]
Теперь для диапазона low.. high
(с low
включением и high
исключением) мы должны определить, для чего нужны элементы, для которых мы должны построить медиану из трех:
L[low]
,L[high-1]
иL[(low+high-1)//2]
.Теперь нам нужно только исправить функцию разбиения на:
def Partition(L, low, high, ascending = True):
print('Quicksort, Parameter L:')
print(L)
result = 0
pivot = median_of_three(L[low], L[(low+high-1)//2], L[high-1])
i = low + 1
for j in range(low + 1, high, 1):
result += 1
if (ascending and L[j] < pivot) or (not ascending and L[j] > pivot):
L[i], L[j] = L[j], L[i]
i += 1
L[low], L[i-1] = L[i-1], L[low]
return i - 1, result
EDIT: определение медианы трех элементов.
Медиана трех элементов - это элемент, который находится в середине двух других значений. Поэтому в случае a <= b <= c
, то b
является медианным.
Поэтому нам нужно определить, в каком порядке элементы, такие, что мы можем определить элемент в середине. Подобно:
def median_of_three(a, b, c):
if a <= b and b <= c:
return b
if c <= b and b <= a:
return b
if a <= c and c <= b:
return c
if b <= c and c <= a:
return c
return a
Итак, теперь мы определили медиану трех с четырьмя, if
это случаев.
EDIT2: Проблема с этим еще не решена. После того, как вы выполнили поворот, вы поменяете элемент L[i-1]
на L[low]
в исходном коде (расположение точки поворота). Но это, конечно, больше не работает: поскольку ось теперь может быть расположена в любом из трех измерений. Поэтому нам нужно сделать median_of_three(..)
умнее: он должен не только возвращать элемент поворота, но и местоположение этого стержня:
def median_of_three(L, low, high):
mid = (low+high-1)//2
a = L[low]
b = L[mid]
c = L[high-1]
if a <= b <= c:
return b, mid
if c <= b <= a:
return b, mid
if a <= c <= b:
return c, high-1
if b <= c <= a:
return c, high-1
return a, low
Теперь мы можем решить эту проблему:
def Partition(L, low, high, ascending = True):
print('Quicksort, Parameter L:')
print(L)
result = 0
pivot, pidx = median_of_three(L, low, high)
i = low + (low == pidx)
for j in range(low, high, 1):
if j == pidx:
continue
result += 1
if (ascending and L[j] < pivot) or (not ascending and L[j] > pivot):
L[i], L[j] = L[j], L[i]
i += 1 + (i+1 == pidx)
L[pidx], L[i-1] = L[i-1], L[pidx]
return i - 1, result
EDIT3: очистка.
Хотя вышеописанное, похоже, работает, оно довольно сложно: нам нужно дать i
и j
"пропустить" местоположение стержня.
Это, вероятно, проще, если мы сначала переместим ось вращения к фронту подсписок (так к low
индексу):
def Partition(L, low, high, ascending = True):
print('Quicksort, Parameter L:')
print(L)
result = 0
pivot, pidx = median_of_three(L, low, high)
L[low], L[pidx] = L[pidx], L[low]
i = low + 1
for j in range(low+1, high, 1):
result += 1
if (ascending and L[j] < pivot) or (not ascending and L[j] > pivot):
L[i], L[j] = L[j], L[i]
i += 1
L[low], L[i-1] = L[i-1], L[low]
return i - 1, result
В "медиане трех" версий quicksort вы не только хотите найти медиану, чтобы использовать ее в качестве точки опоры, но также хотите разместить максимальные и минимальные значения в своих местах, чтобы некоторые из них были сделаны. Другими словами, вы хотите отсортировать эти три элемента в этих трех местах. (Некоторые варианты не хотят, чтобы они сортировались обычным способом, но я буду придерживаться более простой для вас версии для вас здесь.)
Вероятно, вы не хотите делать это в функции, так как вызовы функций довольно дороги в Python, и эта особенность не очень полезна. Таким образом, вы можете сделать такой код. Скажем, три значения, которые вы хотите отсортировать, находятся в индексах i
, j
и k
, причем i < j < k
. На практике вы, вероятно, будете использовать low
, low + 1
и high
, но вы можете внести эти изменения по своему усмотрению.
if L(i) > L(j):
L(i), L(j) = L(j), L(i)
if L(i) > L(k):
L(i), L(k) = L(k), L(i)
if L(j) > L(k):
L(j), L(k) = L(k), L(j)
Есть некоторые оптимизации, которые можно сделать. Например, вы, вероятно, захотите использовать медианное значение в сводном процессе, поэтому вы можете изменить код, чтобы сохранить окончательное значение L(j)
в простой переменной, что уменьшает поиск в массиве. Обратите внимание, что вы не можете сделать это менее чем в трех сравнениях в целом - вы не можете уменьшить его до двух сравнений, хотя в некоторых особых случаях вы можете это сделать.