【发布时间】:2018-09-02 19:32:42
【问题描述】:
我开始学习 Clojure,并决定在 HackerRank 上做一些项目是一个很好的方法。我发现我的 Clojure 解决方案非常慢。我假设那是因为我仍然在思考,或者只是对 Clojure 的运作方式知之甚少。我写解决方案的最新问题是归零 II。这是我的 Java 代码
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Solution {
private static final int MAX_NUMBER = 1000000;
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static int[] precompute() {
int[] values = new int[MAX_NUMBER];
values[0] = 0;
values[1] = 1;
for (int i = 1; i < MAX_NUMBER; i += 1) {
if ((values[i] == 0) || (values[i] > (values[i - 1] + 1))) {
values[i] = (values[i - 1] + 1);
}
for (int j = 1; j <= i && (i * j) < MAX_NUMBER; j += 1) {
int mult = i * j;
if ((values[mult] == 0) || (values[mult] > (values[i] + 1))) {
values[mult] = values[i] + 1;
}
}
}
return values;
}
public static void main(String[] args) throws Exception {
int numQueries = Integer.parseInt(reader.readLine());
int[] values = Solution.precompute();
for (int loop = 0; loop < numQueries; loop += 1) {
int query = Integer.parseInt(reader.readLine());
System.out.println(values[query]);
}
}
}
我的 Clojure 实现是
(def MAX-NUMBER 1000000)
(defn set-i [out i]
(cond
(= 0 i) (assoc out i 0)
(= 1 i) (assoc out i 1)
(or (= 0 (out i))
(> (out i) (inc (out (dec i)))))
(assoc out i (inc (out (dec i))))
:else out))
(defn set-j [out i j]
(let [mult (* i j)]
(if (or (= 0 (out mult)) (> (out mult) (inc (out i))))
(assoc out mult (inc (out i)))
out)))
;--------------------------------------------------
; Precompute the values for all possible inputs
;--------------------------------------------------
(defn precompute []
(loop [i 0 out (vec (repeat MAX-NUMBER 0))]
(if (< i MAX-NUMBER)
(recur (inc i) (loop [j 1 new-out (set-i out i)]
(if (and (<= j i) (< (* i j) MAX-NUMBER))
(recur (inc j) (set-j new-out i j))
new-out)))
out)))
;--------------------------------------------------
; Read the number of queries
;--------------------------------------------------
(def num-queries (Integer/parseInt (read-line)))
;--------------------------------------------------
; Precompute the solutions
;--------------------------------------------------
(def values (precompute))
;--------------------------------------------------
; Read and process each query
;--------------------------------------------------
(loop [iter 0]
(if (< iter num-queries)
(do
(println (values (Integer/parseInt (read-line))))
(recur (inc iter)))))
Java 代码在我的机器上运行大约需要 1/10 秒,而 Clojure 代码需要接近 2 秒。由于它是同一台机器,具有相同的 JVM,这意味着我在 Clojure 中做错了。
人们如何尝试翻译这种类型的代码?是什么导致它这么慢?
【问题讨论】:
-
你是如何运行你的代码的?您是在使用
lein,将.clj 文件传递给clojure还是其他方式? -
我只是想补充一点:我没有看问题陈述,但您正在使用
loop/recur进行迭代。 Clojure 更喜欢for、reduce和map。如果您寻找 clojure 性能提示,还有更多内容,例如类型提示。 -
(vec (repeat 1e6 0))非常昂贵。它正在创建一百万个充满 0 的元素向量。仅在我的低频手机上就需要整整一秒钟。另一方面,(int-array 1e6 0)需要 14 毫秒。如果您确实需要存储这么多数据,那么数组可能是更好的选择。不过,我无法在您的代码中看到任何效率过低的地方。不过,我会说,如果您将其发布在 Code Review 上,如果您愿意,我会在明天对其进行审查。好像有几个地方可以提建议。 -
从我读过的一些内容中,我得到印象循环/重复执行比减少更好。它基本上是一个嵌套循环,它设置 A[i],然后 for j
-
似乎 map 应该是要走的路,但我看不出如何让 map 设置多个值或如何链接它以使结果向量具有正确的条目。
标签: clojure