我们可以改进这个o(mn)bin计数数字算法吗?

时间:2015-08-21 15:15:26

标签: c# algorithm performance

我在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很大,我希望它尽可能快。

3 个答案:

答案 0 :(得分:2)

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

这是在lastIdx中完成的,其中O(mlogm + nlogm)m的数量和data个区间数。

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

Bonus2 :最优性证明

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

元素明显问题:

  

给定数组data - 找出是否有a1,a2,...,an这样的数据   i,j

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

减少

给定元素清晰度问题i!=j, ai=aj的实例 - 创建数据= a1,...,an和间隔:a1,...,an - 并运行算法。
如果有超过[a1,a1], [a2,a2],..., [an,an]个匹配项 - 有重复项,否则没有。

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

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

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];
    }
}

计算数据中每个值的出现次数,然后对范围边界之间的计数求和。