algorithm - 在 clojure 中有效地映射向量的一部分

标签 algorithm data-structures vector clojure

我想知道如何在 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 也会减慢我的程序。

最佳答案

Concat 和 split-at 都将输入转换为 seq,实际上是一个链表表示,时间复杂度为 O(x)。以下是如何使用向量实现 O(n) 性能。

user> (defn inc-last-n
        [n x]
        (let [count (count x)
              update (fn [x i] (update-in x [i] inc))]
          (reduce update x (range (- count n) count))))
#'user/inc-last-n
user> (inc-last-n 3 [0 1 2 3 4 5 6])
[0 1 2 3 5 6 7]

这将在非关联输入(如 seq/lazy-seq)上失败,因为在非关联类型中没有 O(1) 访问时间。

关于algorithm - 在 clojure 中有效地映射向量的一部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20462791/

相关文章:

c++ - 具有多个 vector 的类模板实现

performance - 在 Haskell 中使用高效异或和位计数打包大位向量

math - 求解 AVL 树中节点数的递归关系?

c++ - vector 填充不同的对象

php - 滚动销售平均算法实现 MySQL + PHP

java - DPLL 算法 - 仅建议

java - 逐行解析 2 个文件并需要避免重复(在特殊情况下)

algorithm - 平衡二叉树逻辑

python - 在Python中从双端队列的左侧弹出一组最多N个条目

c++ - Unordered_set迭代器随机