流媒体

2012年5月29日

一组数字的中位数是指当项目按排序顺序排列时,项目数为奇数时,中间的数字,或当项目数为偶数时,中位数是中间两个数字的平均数;例如,{3741265}的中位数是4,{4213}的中值是2.5。计算中值的普通算法同时考虑整个数字集;这个流媒体该算法重新计算数字集每个连续前缀的中值,并可应用于无限序列的前缀。例如,原始数字序列的流媒体是3、5、4、3.5、3、3.5和4。

使用两个堆计算流中值。所有小于或等于当前中间值的数字都位于左侧堆中,其排列方式使最大数字位于堆的根。所有大于或等于当前中值的数字都位于右侧堆中,这样安排的目的是使最小数字位于堆的根。注意,任何一个堆中都可以有等于当前中值的数字。两个堆中的数字计数相差永远不会超过1。

当进程开始时,两个堆最初是空的。输入序列中的第一个数字被添加到其中一个堆中,不管是哪个堆,并作为第一个流中值返回。然后将输入序列中的第二个数字添加到另一个堆中,如果右堆的根小于左堆的根,则交换两个堆,并返回两个数字的平均值作为第二个流中值。

然后开始主要算法。将输入序列中的每个后续数字与当前中值进行比较,如果它小于当前中值,则添加到左侧堆中,如果它大于当前中值,那么添加到右侧堆中;如果输入数字等于当前中值,则将其添加到计数较小的堆中,如果计数相同,则任意添加到其中一个堆中。如果这导致两个堆的计数相差超过1,则会删除较大堆的根并将其插入较小堆中。然后,如果两个堆的计数不同,则将当前中值计算为较大堆的根,如果大小相同,则计算为两个堆根的平均值。

您的任务是编写一个函数来计算序列的流媒体。完成后,欢迎您阅读运行建议的解决方案,或在下面的评论中发布自己的解决方案或讨论练习。

页:1 2

11对“流媒体”的回应

  1. 朱西·皮图莱宁

    Python的heapq库实现了min-heaps。我将较小的数字按在负数中,以便min和max切换角色。必须是最新的Python(可能是我使用的3,也可能是带有适当咒语的2)才能按预期进行除法。

    从heapq导入heappush作为push,heappop作为pop定义中位数(数字):数字=iter(数字)少,多=[],[]第一个=下一个(数字)产量优先秒=下一个(数字)推动(更少,-分钟(第一,第二))推动(更多,最大(第一,第二))而True为真:current=(如果len(less)<len(more),则为more[0]else-less[0]if len(less)>len(more)else(更多[0]-更少[0])/2)屈服电流number=下一个(数字)如果数字<=电流:推动(less,-number)else:推送(more,number)小,大=((少,多)如果len(少)<=len(多)其他(更多,更少)如果len(大)-len(小)>1:push(小,-pop(大))如果__name__=='__main__':打印(元组(中位数((3,7,4,1,2,6,5)))#打印(3、5.0、4、3.5、3、3.5、4)

    (我过去一直对三的中位数的关联性感到困惑。毕竟它不适合这个问题,所以这次我非常谦逊地遵循了指示。希望没有重大错误:)

  2. 迈克

    Python 2.7版

    我保留堆,使其大小相同,或者右侧堆比左侧堆大1个元素。这样,如果正确的堆更大,中间值就是正确堆的顶部;否则,它是两个堆顶部的平均值。

    从heapq导入heappop,heappush定义流媒体(seq):seq=iter(seq)left,right=[],[下一个(seq)]而True为真:如果len(right)>len(left)else(right[0]-left[0])/2.0,则屈服于right[0]heappush(左,-heappushpop(右,下一个(seq)))如果len(左)>len(右):heappush(右,-heappop(左))
  3. 迈克

    对不起,进口行错了。以下是更正后的列表:

    从heapq导入heappop、heappush和heappushpop定义流媒体(seq):seq=iter(seq)left,right=[],[下一个(seq)]而True为真:如果len(right)>len(left)else(right[0]-left[0])/2.0,则生成right[0]heappush(左,-heappushpop(右,下一个(seq)))如果len(左)>len(右):heappush(右,-heappop(左))
  4. 尤西·皮伊图莱宁

    迈克,很好。这里与Scheme中的相同,假设变化的min-heap操作make-heap,size,least,push!,砰!。它对列表中的每个连续中位数应用一个过程。(未测试。我既没有堆操作也没有时间。)

    (定义(用于每个中点的进程编号)(何时(配对数)(proc(车号))(何时(配对?(cdr编号))(let(左(make-heap))(右(make-heap)(向右推(cadr数字))(每个(λ(数量))(proc(if(<(左尺寸)(右尺寸))(最右边)(/(-(最小右)(最小左))(推!左(-(弹出!右(推!右数字)))(如果(>(左尺寸)(右尺寸))(推!右(-(弹出!左)))(cddr编号)))
  5. 拉杜

    (定义流媒体
    (λ(ls)
    (定义中间带
    (λ(ls)
    (第二
    [(=(长度ls)1)(车厢ls)]
    [(=(长度ls)2)(/(+(轿厢ls)(cadr ls))2)]
    [其他(中值(cdr(反向(cdr)(反向(排序!<ls))))])
    (让([sls'()][med'()])
    (do([ls ls(cdr ls)])
    ((null?ls)(反向med))
    (设置!sls(cons(car ls)sls))
    (set!med(cons(median(list-copy sls))med)))

  6. 迈克

    今天早上我突然想到,通过将循环展开一次迭代,可以消除比较堆大小的步骤。

    从heapq导入push,pushpop定义流媒体(seq):seq=iter(seq)左,右=[],[]而True为真:push(右,-pushpop(左,-next(seq))让渡权[0]push(左,-pushpop(右,下一个(seq))产量(右[0]-左[0])/2.0

    如果一个函数每次从输入流“馈送”一个元素,并返回到目前为止馈送到该函数的所有元素的中值,那么它可能也很有用。可以这样做:

    从堆导入推送定义smedian():def生成():左,右=[],[(产量)]而True为真:push(左,-pushpop(右,(右屈服[0]))push(右,-pushpop(左,-(屈服((右[0]-左[0])/2.0)))g=发电机()下一个(g)返回g#例如:mediansofar=smedian()对于(3,7,4,1,2,6,5)中的n:打印或发送(n)的媒体,#输出=>3 5.0 4 3.5 3 3.5 4
  7. 这是一种不适定(尽管很聪明)的解决方案。如果我们真的在处理一个数字流,那么将整个数据流存储在内存中是不可行的,而这个解决方案要求我们在进行过程中存储整个历史记录。当然,如果生成数字的过程没有随时间变化,那么我们可以在一段时间后中止此计算,并确信我们的流中值已经收敛到真正中值的适当近似值。但流媒体的关键在于,当生成数字的过程发生变化时,允许媒体随时间而变化。

    这里有一个更合适的解决方案,它在实践中使用,只需要O(1)空间(我看到的最早对该方法的引用是在McFarlane中,“图像中小猪的分割和跟踪”)。保留一个表示当前中值的数字m。也许将其初始化为序列的前几个元素的中值,或者替换为序列中的第一个元素,或者一些合理的猜测。然后,当一个新元素x到达时,如果x大于m,则将m增加固定量,如果x小于m,则减少固定量。很明显,随着更多元素的到来,m将收敛到真正的中值,直至增量大小的选择。

    我想可以添加额外的点检查技术来动态更新增量,但在我的应用程序中,我只处理整数序列,所以增量大小为1就足够了。此外,正如预期的那样,该技术的收敛速度没有堆方法快,尤其是m的初始选择较差。另一方面,它的收敛速度可能与流中值算法的收敛速度一样快。

  8. 帕拉格古普塔

    http://www.spoj.pl/problems/WEIRDFN/
    这将是一个很好的问题,可以尝试使用流媒体实现。

  9. […]以前研究过计算数字流中值的流中值和滑动中值的算法;流媒体需要[…]

  10. […]使用两个堆计算流中值。所有小于或等于当前中值的数字都在[…]中

  11. […]mediana de transmissión se calcula utilizando dos montones(利用多斯蒙顿)。Todos los números menores o iguales是一个真正的媒体[…]

留下评论