理解使用堆从整数流中获取均值

时间:2017-07-20 22:13:24

标签: python algorithm heap

我正在关注以下博客并了解如何以非常微妙的方式获得中位数。博客是here

现在,我将以下函数添加到streamMedian类中以获取插入的数字的平均值而不是获得所需的输出

import heapq

class streamMedian:
    def __init__(self):
        self.minHeap, self.maxHeap = [], []
        self.N=0


    def insert(self, num):
        if self.N%2==0:
            heapq.heappush(self.maxHeap, -1*num)
            self.N+=1
            if len(self.minHeap)==0:
                return
            if -1*self.maxHeap[0]>self.minHeap[0]:
                toMin=-1*heapq.heappop(self.maxHeap)
                toMax=heapq.heappop(self.minHeap)
                heapq.heappush(self.maxHeap, -1*toMax)
                heapq.heappush(self.minHeap, toMin)
        else:
            toMin=-1*heapq.heappushpop(self.maxHeap, -1*num)
            heapq.heappush(self.minHeap, toMin)
            self.N+=1

    def getMedian(self):
        if self.N%2==0:
            return (-1*self.maxHeap[0]+self.minHeap[0])/2.0
        else:
            return -1*self.maxHeap[0]

    def getMean(self):
        sum = 0
        for num in self.maxHeap:
            sum += num
        for num in self.minHeap:
            sum += num 
        return sum/self.N

这是对streamMedian类的函数调用。

test = streamMedian()
test.insert(1)
test.insert(2)
test.insert(3)
print test.getMedian()
print test.getMean()

此处的中位数应为2,平均值应为2(而输出为0)。提前致谢。

2 个答案:

答案 0 :(得分:2)

您正在将负数推送到maxHeap-1*num)。 您需要在getMean()中反转,例如:

def getMean(self):
    total = 0
    for num in self.maxHeap:
        total -= num
    for num in self.minHeap:
        total += num 
    return total/self.N

或者:

def getMean(self):
    return (abs(sum(self.maxHeap)) + sum(self.minHeap))/self.N

注意:不要将sum用作隐藏python内置sum()函数的变量。

答案 1 :(得分:1)

AChampion的回答正确识别您当前代码的问题,并在仍然使用您当前的算法时提供合理的解决方案。但是,该算法效率不高(需要O(N)次)并且您可以做得更好。

具体来说,除了将其推入其中一个堆之外,您应该将您要插入的值添加到累积总和中。这样,当你需要得到一个均值时,你可以在恒定时间内计算它(只需一个分区):

class streamMedian:
    def __init__(self):
        self.minHeap, self.maxHeap = [], []
        self.cumulative_sum = 0.0             # new instance variable
        self.N=0


    def insert(self, num):
        self.cumulative_sum += num            # add each value to it
        # rest of insert code...

    # median code...

    def getMean(self):
        return self.cumulative_sum / self.N   # compute the mean in constant time

请注意,如果您正在使用Python 2(它看起来像你),那么使用浮点值cumulative_sum而不是整数{{来初始化0.0非常重要。 1}}(否则会很自然)。当你在Python 2中划分两个整数时,你将得到另一个整数,向下舍入。如果你计算01(你期待2的平均值,那么这可能是不可取的,但你得到{{} {1}}如果你只做1.5)。 Python 3做得更好(你总是从常规部门得到一个浮点数,并且可以使用1运算符明确请求" floor" division)。如果您想在Python 2中获得相同的语义,可以将(1 + 2) / 2放在模块的顶部。