【问题标题】:Can we improve this o(mn) algorithm of bin-counting numbers?我们可以改进这个 o(mn) 的 bin 计数算法吗?
【发布时间】:2015-08-21 15:15:26
【问题描述】:

我在 C# 中有一个数组,其中包含数字(例如 int、float 或 double);我有另一个范围数组(每个都定义为下限和上限)。我目前的实现是这样的。

        foreach (var v in data)
        {
            foreach (var row in ranges)
            {
                if (v >= row.lower && v <= row.high)
                {
                    statistics[row]++;
                    break;
                }
            }
        }

所以算法是 O(mn),其中 m 是范围的数量,n 是数字的大小。

这可以改进吗?因为实际上,n 很大,我希望它尽可能快。

【问题讨论】:

  • 范围是否重叠?
  • 理想情况下,是的,它们可能会重叠。但是,如果我们可以基于它们不能重叠的事实来非常快速地获得一些东西,那么我很乐意接受。
  • 如果您有重叠范围,您可以调整为非重叠范围提供的算法以使其仍然有效。获取您开始使用的范围列表并导出非重叠“子范围”列表,其中子范围具有下限、上限以及与该子范围重叠的原始范围列表。将此子范围列表提供给以下算法之一以获得子范围的直方图,然后对于 S 列出的每个原始范围 R 的每个子范围 S,将 S 的总数添加到 R;这给了我们原始范围内的直方图。

标签: c# algorithm performance


【解决方案1】:

data 数组进行排序,然后对于每个间隔 - 在data 中找到此范围内的第一个索引,以及最后一个索引(均使用二进制搜索)。通过减少lastIdx-firstIdx(或添加+1,取决于lastIdx 是否包含),可以轻松计算此区间内的元素数量。

这是在O(mlogm + nlogm) 中完成的,其中mdata 的数量,n 是间隔的数量。

奖励:如果data 不断变化,您可以使用order statistics tree,方法相同(因为此树允许您轻松找到每个元素的索引,并且支持修改数据)。

Bonus2:最优性证明

使用基于比较的算法,这不能做得更好,因为如果可以,我们也可以更好地解决element distinctness problem

元素区别问题:

给定一个数组a1,a2,...,an - 找出是否有i,j 这样 i!=j, ai=aj.

这个问题已知有Omega(nlogn) time bound 使用基于比较的算法。

减少:

给定元素区别问题a1,...,an 的实例 - 创建数据=a1,...,an 和区间:[a1,a1], [a2,a2],..., [an,an] - 并运行算法。
如果有超过n 匹配 - 有重复,否则没有。

上述算法的复杂度是O(n+f(n)),其中n是元素个数,f(n)是这个算法的复杂度。这必须是Omega(nlogn)f(n) 也是如此,我们可以得出结论,没有更有效的算法。

【讨论】:

    【解决方案2】:

    假设范围是有序的,你总是选择第一个适合的范围,对吧?

    这意味着您可以轻松构建下界的二叉树。您找到低于您的数字的最高下限,并检查它是否符合上限。如果树是适当平衡的,这可以让你非常接近 O(nlog m)。当然,如果您不需要经常更改范围,一个简单的有序数组就可以了——只需使用通常的二分查找方法即可。

    使用哈希表可以让您非常接近 O(n),具体取决于范围的结构。如果还订购了data,您可以获得更好的结果。

    【讨论】:

    • 看起来,如果你迭代data,你会得到 O(nlog m)。或者,使用哈希表,O(n)。还是我读错了? (我问的原因是 OP 表明 n 很大,所以它是 log m 还是 log n 很重要。
    • @ErickG.Hagstrom 是的,对 - 我假设 m 是数字,而不是范围。
    【解决方案3】:

    不涉及数据排序的替代解决方案:

    var dictionary = new Dictionary<int, int>();
    
    foreach (var v in data) {
        if (dictionary.ContainsKey(v)){
            dictionary[v]++;
        } else {
            dictionary[v] = 1;
        }
    }
    
    foreach (var row in ranges) {
        for (var i = row.lower; i <= row.higher; i++) {
            statistics[row] += dictionary[i];
        }
    }
    

    获取数据中每个值的出现次数,然后将范围内的计数相加。

    【讨论】:

      猜你喜欢
      • 2022-11-19
      • 1970-01-01
      • 1970-01-01
      • 2021-11-10
      • 2023-04-10
      • 2021-12-20
      • 1970-01-01
      • 2016-01-27
      • 2021-11-23
      相关资源
      最近更新 更多