计算集合中元素频率的复杂性是多少?

时间:2012-10-18 06:37:23

标签: clojure functional-programming reduce

以下是frequenciesclojure的实施:

(defn frequencies
  "Returns a map from distinct items in coll to the number of times
  they appear."
  [coll]
  (persistent!
   (reduce (fn [counts x]
         (assoc! counts x (inc (get counts x 0))))
           (transient {}) coll)))

assoc!是否被视为突变?

assoc!frequencies的复杂性是多少?

此外,似乎counts在每次迭代中被访问两次:它是否会导致性能下降?

3 个答案:

答案 0 :(得分:4)

assoc!是一个瞬态的变异,我认为它是O(log n)摊销的。因此frequencies的整个执行是O(n log n)。

counts是一个局部绑定变量,因此访问它两次都没问题。

以下是不使用任何多重状态的频率的功能版本:

(defn frequencies-2 [coll]
  (reduce (fn [m v] (assoc m v (inc (get m v 0)))) {} coll))

此功能版本也是O(n log n),但由于创建和丢弃更多临时对象,它会有更多的开销(更高的常数因子)。

答案 1 :(得分:2)

您可以使用树将地图从元素存储到具有log(n)复杂度的频率(它可以是二叉搜索树,AVL,红黑树等)。 选择此树的功能实现,即您不能改变它,而是assoc counts x freq返回一个新的数据结构,在memomry中共享与counts的公共部分。这是一种“写作复制”。 然后计算所有频率的性能将是O(n log(n))。

答案 2 :(得分:1)

assoc!会改变瞬态数据,其性能远远优于assoc。它并不违反不可变的Clojure模型(见http://clojure.org/transients)。

  1. persistent!transient为O(1)
  2. assoc!是O(log32 n),它实际上是O(1),因为hash-map的上限是~2 ^ 32项的大小,这使得最大树深度为6
  3. 因此,frequencies的复杂性与coll的大小呈线性关系。

    备注:正如@mikera所注意到的,frequencies的复杂性也与assoc呈线性关系,但具有更高的常数因子。