Питон: быстрая сортировка с медианой из трех

1

Я пытаюсь изменить этот код 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
Теги:
pivot
sorting
quicksort
median

2 ответа

2
Лучший ответ

Давайте сначала реализуем медианную из трех для трех чисел, поэтому независимая функция. Мы можем это сделать, сортируя список из трех элементов, а затем возвращаем второй элемент, например:

def median_of_three(a, b, c):
    return sorted([a, b, c])[1]

Теперь для диапазона low.. highlow включением и high исключением) мы должны определить, для чего нужны элементы, для которых мы должны построить медиану из трех:

  1. первый элемент: L[low],
  2. последний элемент L[high-1] и
  3. средний элемент (в случае двух таких, возьмите первый) 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
  • 0
    Хотя это работает, назначение не хочет, чтобы мы использовали какие-либо функции Python. Так что сортировка () не вариант. Вот почему я тоже колебался с функциями статистики.
  • 0
    @Dennis: я уверен, что использование статистических функций также не разрешено. Но здесь есть в основном шесть способов упорядочить элементы. Поэтому я советую вам самим «раскрыть» функцию в качестве упражнения.
Показать ещё 22 комментария
1

В "медиане трех" версий 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) в простой переменной, что уменьшает поиск в массиве. Обратите внимание, что вы не можете сделать это менее чем в трех сравнениях в целом - вы не можете уменьшить его до двух сравнений, хотя в некоторых особых случаях вы можете это сделать.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню