找到动态增长范围中位数的最快方法

Fastest way to find median in dynamically growing range

本文关键字:方法 中位数 范围 动态      更新时间:2023-10-16

谁能建议任何方法或链接到实现快速中位数查找动态范围在c++中?例如,假设在我的程序中迭代的范围变大,我想在每次运行时找到中位数。

Range
4
3,4
8,3,4
2,8,3,4
7,2,8,3,4

所以上面的代码最终会为每行生成5个中值。

在不跟踪数组排序副本的情况下,您可以获得的最佳结果是重用旧的中位数,并使用下一个最大值的线性时间搜索来更新它。这听起来可能很简单,然而,有一个问题我们必须解决。

考虑以下列表(为了更容易理解,对它们进行了排序,但您保持它们的任意顺序):

    1, 2, 3, 3, 3, 4, 5
//           *

所以这里,中位数是3(因为列表是排序的,所以是中间的元素)。现在,如果你添加一个比中位数大的数字,这可能会将中位数向右"移动"一半。我看到了两个问题:我们怎样才能前进半个指数?(根据定义,中位数是后面两个值的平均值。)当我们只知道中值是3时,我们怎么知道中值是3呢?

这可以通过不仅存储当前中位数而且存储中位数的位置来解决,这里它有一个"索引偏移量" 1,因为它是第二个3。在列表中添加一个大于或等于3的数字会将索引偏移量更改为1.5。添加一个小于3的数字将其更改为0.5

当这个数字小于零时,中位数发生变化。如果它超过相等数的计数(- 1),在本例中是2,这意味着新的中位数大于上一个相等数,则必须更改。在这两种情况下,您都必须搜索下一个更小/更大的数字并更新中值。为了始终知道索引偏移量的上限是多少(在本例中为2),您还必须跟踪相等数字的计数。

这应该能让你大致了解如何在线性时间内实现中值更新。

我认为你可以使用最小-最大-中位数堆。每次更新数组时,您只需要log(n)时间来找到新的中值。对于最小最大中值堆,根是中值,左边树是最小最大堆,而右边树是最大最小堆。有关详细信息,请参阅论文"最小最大堆和通用优先队列"。

在下面的一些代码中,我重新编写了这个堆栈以提供您所需的输出

    private void button1_Click(object sender, EventArgs e)
    {
        string range = "7,2,8,3,4";
        decimal median = FindMedian(range);
        MessageBox.Show(median.ToString());
    }
    public decimal FindMedian(string source)
    {
        // Create a copy of the input, and sort the copy
        int[] temp = source.Split(',').Select(m=> Convert.ToInt32(m)).ToArray();
        Array.Sort(temp);
        int count = temp.Length;
        if (count == 0) {
            throw new InvalidOperationException("Empty collection");
        }
        else if (count % 2 == 0) {
            // count is even, average two middle elements
            int a = temp[count / 2 - 1];
            int b = temp[count / 2];
            return (a + b) / 2m;
        }
        else {
            // count is odd, return the middle element
            return temp[count / 2];
        }
    }