我在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很大,我希望它尽可能快。
答案 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];
}
}
计算数据中每个值的出现次数,然后对范围边界之间的计数求和。