我已经做了一些黑客挑战,并注意到我似乎无法编写有效的代码,因为我经常会遇到超时,即使通过测试的答案是正确的。例如,对于this challenge,这是我的代码:
(let [divisors (fn [n] (into #{n} (into #{1} (filter (comp zero? (partial rem n)) (range 1 n)))))
str->ints (fn [string]
(map #(Integer/parseInt %)
(clojure.string/split string #" ")))
;lines (line-seq (java.io.BufferedReader. *in*))
lines ["3"
"10 4"
"1 100"
"288 240"
]
pairs (map str->ints (rest lines))
first-divs (map divisors (map first pairs))
second-divs (map divisors (map second pairs))
intersections (map clojure.set/intersection first-divs second-divs)
counts (map count intersections)
]
(doseq [v counts]
(println (str v))))
请注意,clojure/set
在hackerrank中不存在。我只是为了简洁而放在这里。
答案 0 :(得分:3)
在这个确切的情况下,明显误用map
函数:
虽然clojure系列是懒惰的,但对它们的操作仍然不是免费的。所以当你链接很多map
时,你仍然拥有所有的中间集合(这里有7个)。为了避免这种情况,人们通常会使用传感器,但在你的情况下,你只是将每个输入线映射到一个输出线,所以它足以在输入集合中一次传递:
(let [divisors (fn [n] (into #{n} (into #{1} (filter (comp zero? (partial rem n)) (range 1 n)))))
str->ints (fn [string]
(map #(Integer/parseInt %)
(clojure.string/split string #" ")))
;lines (line-seq (java.io.BufferedReader. *in*))
get-counts (fn [pair] (let [d1 (divisors (first pair))
d2 (divisors (second pair))]
(count (clojure.set/intersection d1 d2))))
lines ["3"
"10 4"
"1 100"
"288 240"
]
counts (map (comp get-counts str->ints) (rest lines))]
(doseq [v counts]
(println (str v))))
这里不讨论整个算法的正确性。也许它也可以进行优化。但是就clojure的机制而言,这种改变应该会非常显着地加速你的代码。
<强>更新强>
对于算法,您可能希望首先将范围从1..n限制为1 ..(sqrt n),将x
和n/x
同时添加到结果集中{ {1}}是x
的除数,它可以为大数字带来巨大的利润:
n
我也会考虑找到两个数中至少两个数的所有除数,然后保持其他数字可被整除的除数。这将消除对更大数量的除数的搜索。
(defn divisors [n]
(into #{} (mapcat #(when (zero? (rem n %)) [% (/ n %)])
(range 1 (inc (Math/floor (Math/sqrt n)))))))
如果仍然无法通过测试,你应该寻找一些很好的公约数算法。
更新2
将更新的算法提交给hackerrank并且现在通过了