【问题标题】:What is the complexity of calculating frequencies of elements in a collection?计算集合中元素频率的复杂性是多少?
【发布时间】:2012-10-08 13:12:47
【问题描述】:

这里是clojurefrequencies的实现:

(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 似乎在每次迭代中被访问两次:它会导致性能损失吗?

【问题讨论】:

    标签: clojure functional-programming reduce


    【解决方案1】:

    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),尽管由于创建和丢弃更多临时对象,它会产生更多开销(更高的常数因子)。

    【讨论】:

    • assoc!assoc 实际上是 O(1):请参阅下面的答案。
    • @viebel - 当然,在这种情况下,log32 n 足够小,您可以考虑在需要 O(1) 操作的地方使用它来实现最实际的目的。然而,它仍然不如许多真正的 O(1) 操作(例如 HashMap put)快,并且从技术上讲,该算法仍然具有 O(log n) 复杂度。因此,我认为将其描述为 O(1) 有点虚伪。
    【解决方案2】:

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

    【讨论】:

    • 谢谢!我稍微修改了这个问题。看看吧!
    【解决方案3】:

    assoc! 对瞬态数据进行变异,它的性能比assoc 好得多。这并没有真正违反不可变 Clojure 的模型(请参阅http://clojure.org/transients)。

    1. persistent!transient 是 O(1)
    2. assoc! 是 O(log32 n),实际上是 O(1),因为 hash-map 的上限约为 2^32 个项目,因此最大树深度为 6

    因此,frequencies 的复杂度与coll 的大小成线性关系。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-04
      • 2012-08-31
      • 2014-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多