【问题标题】:Why my python version hoare partition quick sort is slower than lomuto partition?为什么我的python版本hoare分区快速排序比lomuto分区慢?
【发布时间】:2021-01-31 04:43:09
【问题描述】:

通常在实现快速排序时,Hoare 的方案比 Lomuto 的分区方案更有效,因为平均交换次数更少。为了验证这一点,我尝试用重复元素计算所有可能排列中的交换。我生成了一个包含 8 个元素的所有可能重复排列的列表来测试,总共是 8 个排列的 8 次方。但我机器上的结果很奇怪,Hoare 的方案即使交换次数较少,也较慢。这是python代码:

import copy
import time

swap_lomuto = 0
swap_hoare = 0

def hoare_partition(a, p, r):
    global swap_hoare
    pivot = a[(p+r)//2]
    i = p - 1
    j = r + 1
    while True:
        i = i + 1
        j = j - 1
        while a[i] < pivot:
            i = i + 1
        while a[j] > pivot:
            j = j - 1
        if i < j:
            a[i], a[j] = a[j], a[i]
            swap_hoare += 1
        else:
            return j

def quick_sort_hoare(a, lo, hi):
    if lo < hi:
        p = hoare_partition(a, lo, hi)
        quick_sort_hoare(a, lo, p)
        quick_sort_hoare(a, p+1, hi)


def lomuto_partition(a, p, r):
    global swap_lomuto
    pivot = a[r]
    i = j = p
    while j < r:
        if a[j] < pivot:
            a[i], a[j] = a[j], a[i]
            i = i + 1
            swap_lomuto += 1
        j = j + 1
    a[i], a[r] = a[r], a[i]
    swap_lomuto +=1
    return i

def quick_sort_lomuto(a, lo, hi):
    if lo < hi:
        p = lomuto_partition(a, lo, hi)
        quick_sort_lomuto(a, lo, p-1)
        quick_sort_lomuto(a, p+1, hi)

def gen_cases(n):
    a = list(range(1, n+1))
    r = [[]]
    def product(A, B):
        result = []
        for a in A:
            for b in B:
                result.append(a + [b])
        return result
    for i in range(n):
        r = product(r, a)
    return r

# generate 8**8 permutations with repeated elements
# e.g. [[1, 1], [1, 2], [2, 1], [2, 2]] when n == 2
n = 8
cases1 = gen_cases(n)
cases2 = copy.deepcopy(cases1)

start = time.time()
for c in cases1:
    quick_sort_lomuto(c, 0, len(c)-1)
end = time.time()
print("lomuto time: ", end-start)
print("lomuto swaps: ", swap_lomuto)

start = time.time()
for c in cases2:
    quick_sort_hoare(c, 0, len(c)-1)
end = time.time()
print("hoare time: ", end-start)
print("hoare swaps: ", swap_hoare)

# assert if two methods give the same result
for i in range(len(cases1)):
    for j in range(n):
        assert cases1[i][j] == cases2[i][j]

我机器上的结果是

洛穆托时间:46.35845708847046
lomuto 掉期:199594736
暂停时间:59.4106240272522
霍尔掉期:116165488

我不知道哪里错了。

【问题讨论】:

    标签: python sorting quicksort


    【解决方案1】:

    看看这个版本是否做得更好。这可能是解释器开销比算法效率影响更大的问题。

    如果对随机数或伪随机数进行排序,我发现 Lomuto 比 Hoare 稍快。

    def qsort(a, lo, hi):
        if(lo >= hi):
            return
        p = a[(lo + hi) // 2]       # pivot, any a[] except a[hi]
        i = lo - 1
        j = hi + 1
        while(1):
            while(1):               # while(a[++i] < p)
                i += 1
                if(a[i] >= p):
                    break
            while(1):               # while(a[--j] < p)
                j -= 1
                if(a[j] <= p):
                    break
            if(i >= j):
                break
            a[i],a[j] = a[j],a[i]
        qsort(a, lo, j)
        qsort(a, j+1, hi)
    

    【讨论】:

    • 感谢您的回答。这个版本仍然比 lomuto 版本慢,尽管它的交换次数更少。此版本在相同测试用例上的运行时间约为 52-55 秒,交换次数为 116165488。似乎函数调用不是主要问题。
    • @abzie - 我使用编译后的 C++ 得到了类似的结果。对 1600 万个 64 位无符号伪随机整数进行排序,使用 Lomuto 大约需要 1.34 秒,使用 Hoare 大约需要 1.38 秒。尽管 Hoare 进行的交换次数较少,但显然它正在读取更多数据,部分原因是枢轴并未从递归调用中排除。不过,Hoare 可以更好地处理重复元素。
    猜你喜欢
    • 2018-05-27
    • 1970-01-01
    • 1970-01-01
    • 2021-06-12
    • 2019-10-18
    • 2015-05-15
    • 2018-09-13
    • 2010-12-06
    • 2015-10-28
    相关资源
    最近更新 更多