计算事件的最有效方法?

时间:2010-03-05 04:12:07

标签: performance algorithm language-agnostic data-structures statistics

我希望在性能关键代码中多次计算熵和互信息。作为中间步骤,我需要计算每个值的出现次数。例如:

uint[] myArray = [1,1,2,1,4,5,2];
uint[] occurrences = countOccurrences(myArray);
// Occurrences == [3, 2, 1, 1] or some permutation of that.
// 3 occurrences of 1, 2 occurrences of 2, one each of 4 and 5.

当然,显而易见的方法是使用关联数组或使用“标准”排序算法(如快速排序)对输入数组进行排序。对于小整数,如字节,代码目前专门用于使用普通的旧数组。

是否有任何聪明的算法比哈希表或“标准”排序算法更有效地执行此操作,例如非常有利于插入更新的关联数组实现,或者当数据具有很多关系?

注意:非稀疏整数只是可能数据类型的一个示例。我想在这里实现一个合理的通用解决方案,但由于只包含整数的整数和结构是常见的情况,如果它们非常有效,我会对这些特定的解决方案感兴趣。

3 个答案:

答案 0 :(得分:3)

正如另一个答案所表明的那样,散列通常更具可扩展性。然而,对于许多可能的分布(以及许多真实情况,其中子阵列恰好经常被排序,取决于整个阵列的组合方式),timsort通常“异常好”(更接近O( N)而不是O(N log N)) - 我听说它可能会成为Java中标准/默认排序算法的一些相当接近的未来数据(多年来它一直是Python中的标准排序算法)。

除了对一些代表您期望遇到的实际工作量的案例进行基准测试之外,没有什么好方法可以解决这些问题(显然存在风险,您可能会选择实际上恰好是的样本)有偏见/无代表性 - 如果您正在尝试构建一个将由您控制之外的许多外部用户使用的库,则风险不小。)

答案 1 :(得分:2)

请详细说明您的数据。

  • 有多少件物品?
  • 独特商品与总商品的预期比率是多少?
  • 整数实际值的分布是什么?它们通常小到足以使用简单的计数阵列吗?或者他们是否聚集成相当狭窄的群体?等

无论如何,我建议采用以下思路:修改mergesort来计算重复数。

也就是说,你的工作不是数字而是成对(数量,频率)(你可能会使用一些聪明的内存效率表示,例如两个数组而不是数组对等)。

你从[(x1,1),(x2,1),...]开始并像往常一样进行合并,但是当你合并两个以相同值开头的列表时,你将值放入输出列出他们的出现总和。在你的例子中:

[1:1,1:1,2:1,1:1,4:1,5:1,2:1]
Split into [1:1, 1:1, 2:1] and [1:1, 4:1, 5:1, 2:1]
Recursively process them; you get [1:2, 2:1] and [1:1, 2:1, 4:1, 5:1]
Merge them: (first / second / output)
[1:2, 2:1] / [1:1, 2:1, 4:1, 5:1] / [] - we add up 1:2 and 1:1 and get 1:3
[2:1] / [2:1, 4:1, 5:1] / [1:3] - we add up 2:1 and 2:1 and get 2:2
[] / [4:1, 5:1] / [1:3, 2:2]
[1:3, 2:2, 4:1, 5:1]

通过使用一些巧妙的技巧来进行数组的初始缩减(可以获得一个值数组:比原始数据小得多的出现对,但每个'值'的'出现'的总和'等于原始数组中'value'的出现次数)。例如,将数组拆分为连续块,其中值相差不超过256或65536,并使用小数组计算每个块内的出现次数。实际上,这个技巧也可以在以后的合并阶段应用。

答案 2 :(得分:1)

使用示例中的整数数组,最有效的方法是使用int数组并使用您的值对其进行索引(如您所看到的那样)。

如果你不能这样做,我想不出比hashmap更好的选择。您只需要一个快速哈希算法。如果要使用所有数据,则不能比O(n)性能更好。是否只使用您拥有的部分数据?

(请注意,排序和计数比使用基于散列映射的解决方案(O(n))渐近地慢(O(n * log(n)))。)