我正在尝试解决Maximum subarray problem on hacker rank。这是一个标准的DP问题,我写了一个O(n)解决方案:
(defn dp
[v]
(let [n (count v)]
(loop [i 1 f (v 0) best f]
(if (< i n)
(let [fi (max (v i) (+ f (v i)))]
(recur (inc i) fi (max fi best)))
best))))
(defn positive-only
[v]
(reduce + (filterv #(> % 0) v)))
(defn line->ints
[line]
(->>
(clojure.string/split line #" ")
(map #(Integer. %))
(into [])
))
(let [T (Integer. (read-line))]
(loop [test 0]
(when (< test T)
(let [_ (read-line)
x (read-line)
v (line->ints x)
a (dp-array v)
b (let [p (positive-only v)]
(if (= p 0) (reduce max v) p))]
(printf "%d %d\n" a b))
(recur (inc test)))))
令我惊讶的是,我对一个大型测试案例的时间有限。我下载了输入文件,发现上面的版本需要大约3秒才能运行。
我认为瓶颈在(v i)
(获得向量v中的第i个元素)。所以我将数据结构从向量更改为数组:
(defn dp-array
[v0]
(let [v (into-array v0)
n (int (alength v))]
(loop [i 1
f (aget v 0)
best f]
(if (< i n)
(let [fi (max (aget v i) (+ f (aget v i)))]
(recur (inc i) fi (max fi best)))
best))))
这个阵列版本甚至更慢。在相同的输入上,它花费33秒,比矢量版本慢得多。我认为缓慢是由于拳击和拆箱。我尝试添加类型提示,但遇到了运行时错误。任何人都可以帮我改进dp-array
功能吗?谢谢!
另外,非常感谢有人知道如何改进矢量版本。
更新
最后,我设法接受了我的clojure程序,而不是通过优化动态编程功能,而是将(Integer. str)
更改为(Integer/parseInt str)
。这样,在从字符串转换为整数时避免了反射。
我还将into-array
替换为int-array
。但两个版本的速度仍然相互提升。我希望数组版本比矢量版本快。
答案 0 :(得分:2)
Clojure编译器无法推断v
函数的数组版本中dp-array
的类型,其参数v0具有未知类型。在评估以下alength
和aget
时,这会导致反映成本。为了避免这些不必要的反射,您必须将into-array
替换为long-array
。