Clojure Mutability:O(1)更新向量的性能

时间:2016-06-11 19:36:21

标签: performance vector clojure functional-programming time-complexity

在Java中,我们假设我有一个整数数组:int[] a = {1, 2, 3, 4, 5};。 如果我想更改数组中的元素,我可以通过更改内存中某个地址的数据来实现:a[2] = 9; => {1, 2, 9, 4, 5}

在Clojure中,我可以有一个向量:(def a [1 2 3 4 5])。 如何更改向量中某个位置的元素,保证最坏情况时间复杂度为O(1)?我已经读过assoc关键字的平均时间复杂度为O(1),但这不是我想要的。另外,我查看了瞬态向量,但我还没有找到一个在O(1)中更新向量的简单例子。

2 个答案:

答案 0 :(得分:4)

在Clojure中,向量是一种特定的数据类型 - 它是不可变的并且有O(log_32(n))更新,返回一个通常与原始结构共享的新向量。

如果需要O(1)更新,可以使用数组,所有Java数据类型都可以从Clojure中使用。我在这里使用into只是为了在repl中有一个易于阅读的表示 - 每次使用都会创建一个新的向量,所以你可能想在性能敏感的代码中避免这种情况。

+user=> (def a (into-array Object [:a 1 :b "m" (java.util.Date.)]))
#'user/a
+user=> a
#object["[Ljava.lang.Object;" 0x456d6c1e "[Ljava.lang.Object;@456d6c1e"]
+user=> (into [] a)
[:a 1 :b "m" #inst "2016-06-11T20:28:05.230-00:00"]
+user=> (aset a 2 'new-contents)
new-contents
+user=> (into [] a)
[:a 1 new-contents "m" #inst "2016-06-11T20:28:05.230-00:00"]

Clojure中有各种functions defined for working with arrays

答案 1 :(得分:3)

Clojure向量在其当前实现中具有O(log_32(n))的复杂度(基数32中的对数)。在数学上,O(log_32(n))与O(log(n))相同。差异不是理论上的,而是实际的:

  • AFAICT,因为Clojure持久性向量不能包含超过40亿个元素,log_32(n)永远不会大于7.因此,在某种程度上,它是恒定时间:)。这说明了Persistent Hash Tries中的分支因子是32(与分支因子为2的许多树数据结构不同)。
  • 常数因素很重要! Java数组的更新将比持久向量更快。你不应该考虑渐近的复杂性,但想知道这个常数因素是否对你来说太大了。如果是,请查看transients