【发布时间】:2013-12-26 02:24:02
【问题描述】:
我想知道如何在 Clojure 中以惯用且有效的方式做到这一点:
1) 给定一个包含 n 个整数的向量: [A0 A1 A2 A3 ... An]
2) 将最后的 x 项加 1(假设 x 为 100),因此向量将变为: [A0 A1 A2 A3 ... (An-99 + 1) (An-98 + 1)... (A n-1 + 1) (An + 1)]
一个简单的实现如下所示:
(defn inc-last [x nums]
(let [n (count nums)]
(map #(if (>= % (- n x)) (inc %2) %2)
(range n)
nums)))
(inc-last 2 [1 2 3 4])
;=> [1 2 4 5]
在这个实现中,基本上你只是通过检查每个项目来将整个向量映射到另一个向量,看看它是否需要增加。
但是,这是一个 O(n) 操作,而我只想更改向量中的最后 x 个项目。理想情况下,这应该在 O(x) 而不是 O(n) 中完成。
我正在考虑使用诸如 split-at/concat 之类的函数来实现它,如下所示:
(defn inc-last [x nums]
(let [[nums1 nums2] (split-at x nums)]
(concat nums1 (map inc nums2))))
但是,我不确定这个实现是 O(n) 还是 O(x)。我是 Clojure 的新手,不太确定在 Clojure 中对持久数据结构进行 concat/split-at 等操作的时间复杂度是多少。
所以我的问题是:
1) 第二个实现的时间复杂度是多少?
2)如果还是O(n),有没有Clojure中只需要O(x)的惯用高效的实现来解决这个问题?
感谢任何评论。谢谢。
更新:
noisesmith 的回答告诉我 split-at 会将向量转换为列表,这是我之前没有意识到的事实。由于我将对结果进行随机访问(在处理向量后调用nth),我希望有一个有效的解决方案(O(x) 时间)同时保留向量而不是列表,否则nth 会减慢我的速度程序也是如此。
【问题讨论】:
-
仔细检查您的问题,我惊讶地发现 split-at 的实现不仅在 O(n) 时间内不起作用(预计是因为它是在非关联数据结构上实现的),但它也会遍历前 (count - n) 个元素(或版本中的前 n 个参数,实际上是 inc-first)两次!
标签: algorithm data-structures vector clojure