我已经完成将堆排序算法实现为Python或Java(或任何其他语言)的任务。由于我并不是真的能熟练使用Python或Java,因此我决定同时使用这两种方法。
但是在这里我遇到了一个问题,程序的运行时间比“应该”高得多。 我的意思是,堆排序应该运行到O(n * log n),并且对于当前以几GHz时钟频率运行的处理器,我没想到该算法会在数组中运行超过2000秒大小为320k
因此,对于我所做的事情,我在Python和Java中从此类伪代码实现了算法(我还尝试了Rosetta Code中的Julia代码,以查看运行时间是否相似,为什么选择Julia?随机选择)
因此,我检查了输出是否存在较小的输入大小问题,例如大小为10、20和30的数组。看来该数组在两种语言/实现中均正确排序。
然后,我使用了实现相同算法的heapq库,再次检查运行时间是否相似。当确实如此时,这让我感到惊讶……但是经过几次尝试,我尝试了最后一件事,即更新Python,然后,使用heapq的程序比以前的程序运行得快得多。实际上,320k阵列大约需要2k秒,而现在大约需要1.5秒。
我重试了算法,问题仍然存在。
这是我实现的Heapsort类:
class MaxHeap:
heap = []
def __init__(self, data=None):
if data is not None:
self.buildMaxHeap(data)
@classmethod
def toString(cls):
return str(cls.heap)
@classmethod
def add(cls, elem):
cls.heap.insert(len(cls.heap), elem)
cls.buildMaxHeap(cls.heap)
@classmethod
def remove(cls, elem):
try:
cls.heap.pop(cls.heap.index(elem))
except ValueError:
print("The value you tried to remove is not in the heap")
@classmethod
def maxHeapify(cls, heap, i):
left = 2 * i + 1
right = 2 * i + 2
largest = i
n = len(heap)
if left < n and heap[left] > heap[largest]:
largest = left
if right < n and heap[right] > heap[largest]:
largest = right
if largest != i:
heap[i], heap[largest] = heap[largest], heap[i]
cls.maxHeapify(heap, largest)
@classmethod
def buildMaxHeap(cls, heap):
for i in range(len(heap) // 2, -1, -1):
cls.maxHeapify(heap, i)
cls.heap = heap
@staticmethod
def heapSort(table):
heap = MaxHeap(table)
output = []
i = len(heap.heap) - 1
while i >= 0:
heap.heap[0], heap.heap[i] = heap.heap[i], heap.heap[0]
output = [heap.heap[i]] + output
heap.remove(heap.heap[i])
heap.maxHeapify(heap.heap, 0)
i -= 1
return output
要记录每个数组大小(10000-320000)的运行时,我在主函数中使用此循环:
i = 10000
while i <= 320000:
tab = [0] * i
j = 0
while j < i:
tab[j] = randint(0, i)
j += 1
start = time()
MaxHeap.heapSort(tab)
end = time()
pprint.pprint("Size of the array " + str(i))
pprint.pprint("Total execution time: " + str(end - start) + "s")
i *= 2
如果您需要其余代码来查看错误可能在哪里,请不要犹豫,我将提供它。只是不想无缘无故地共享整个文件。
如前所述,我期望的运行时间是最坏情况下的运行时间:O(n * log n) 使用现代架构和2.6GHz处理器,我期望大约1秒或更短的时间(因为要求运行时间以纳秒为单位,所以我认为即使1秒仍然太长)
以下是结果:
Python (own) : Java (Own)
Time Size Time Size
593ms. 10k 243ms. 10k
2344ms. 20k 600ms. 20k
9558ms. 40k 1647ms. 40k
38999ms. 80k 6666ms. 80k
233811ms. 160k 62789ms. 160k
1724926ms. 320k 473177ms. 320k
Python (heapq) Julia (Rosetta Code)
Time Size Time Size
6ms. 10k 21ms. 10k
14ms. 20k 21ms. 20k
15ms. 40k 23ms. 40k
34ms. 80k 28ms. 80k
79ms. 160k 39ms. 160k
168ms. 320k 60ms. 320k
And according to the formula the O(n * log n) give me :
40000 10k
86021 20k
184082 40k
392247 80k
832659 160k
1761648 320k
我认为这些结果可用于确定所需的时间,具体取决于机器(理论上)
您可以看到,运行时间长的结果来自我的算法,但是我无法确定代码在哪里,这就是为什么我在这里寻求帮助。 (在Java和Python中运行速度都很慢)(没有尝试在Java lib中使用堆排序是不是有一个可以看到与我的实现的区别,我的糟糕)
非常感谢。
编辑:我忘了补充说,我在MacBook Pro(最新版本的MacOS,i7 2,6GHz)上运行了该程序。以防万一,问题可能还出在代码之外。
编辑2:以下是我在收到答案后对算法所做的修改。该程序的运行速度比以前快了大约200倍,因此,对于大小为320k的数组,它现在仅需2秒即可运行
class MaxHeap:
def __init__(self, data=None):
self.heap = []
self.size = 0
if data is not None:
self.size = len(data)
self.buildMaxHeap(data)
def toString(self):
return str(self.heap)
def add(self, elem):
self.heap.insert(self.size, elem)
self.size += 1
self.buildMaxHeap(self.heap)
def remove(self, elem):
try:
self.heap.pop(self.heap.index(elem))
except ValueError:
print("The value you tried to remove is not in the heap")
def maxHeapify(self, heap, i):
left = 2 * i + 1
right = 2 * i + 2
largest = i
if left < self.size and heap[left] > heap[largest]:
largest = left
if right < self.size and heap[right] > heap[largest]:
largest = right
if largest != i:
heap[i], heap[largest] = heap[largest], heap[i]
self.maxHeapify(heap, largest)
def buildMaxHeap(self, heap):
for i in range(self.size // 2, -1, -1):
self.maxHeapify(heap, i)
self.heap = heap
@staticmethod
def heapSort(table):
heap = MaxHeap(table)
i = len(heap.heap) - 1
while i >= 0:
heap.heap[0], heap.heap[i] = heap.heap[i], heap.heap[0]
heap.size -= 1
heap.maxHeapify(heap.heap, 0)
i -= 1
return heap.heap
它使用与前面给出的相同的主管道
答案 0 :(得分:2)
有趣的是,您发布了计算机的时钟速度-您可以计算出算法所需的实际步骤数...但是您需要了解很多有关实现的知识。例如,在python中,每次创建对象或超出范围时,解释器都会更新基础对象上的计数器,并在这些引用计数达到0时释放内存。相反,您应该查看 relative 速度。
您发布的第三方示例在输入数组长度加倍时显示速度降低然后加倍。这似乎不对,是吗?事实证明,对于这些示例,构建数组的最初工作可能会占据对数组进行排序所花费的时间!
在您的代码中,已经有一条注释可以指出我要说的内容...
heap.remove(heap.heap[i])
此操作将遍历您的列表(从索引0开始)以查找匹配的值,然后将其删除。这已经很糟糕了(如果它按预期工作,那么如果您的代码按预期工作,那么您将在该行上进行320k比较!)。但是,情况变得更糟-从数组中删除对象不是就地修改-删除的对象必须在列表中向前移动后的每个对象。最后,不能保证您实际上正在删除那里的最后一个对象...可能存在重复的值!
这是一个有用的网站,列出了python-https://wiki.python.org/moin/TimeComplexity中各种操作的复杂性。为了尽可能高效地实现算法,您需要将尽可能多的数据结构操作设为O(1)。这是一个示例...这是一些原始代码,大概是用heap.heap作为列表...
output = [heap.heap[i]] + output
heap.remove(heap.heap[i])
做
output.append(heap.heap.pop())
将避免分配新列表,并使用恒定时间操作对旧列表进行变异。 (比起使用O(n)时间insert(0)方法,只向后使用输出要好得多!如果您确实需要命令,可以使用出队对象进行输出以获得appendleft方法)
如果您发布了整个代码,则可能还有很多其他小事情我们可以帮助您。希望这会有所帮助!