加快Clojure以避免超时

时间:2016-07-18 04:58:45

标签: clojure

我已经做了一些黑客挑战,并注意到我似乎无法编写有效的代码,因为我经常会遇到超时,即使通过测试的答案是正确的。例如,对于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中不存在。我只是为了简洁而放在这里。

1 个答案:

答案 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),将xn/x同时添加到结果集中{ {1}}是x的除数,它可以为大数字带来巨大的利润:

n

我也会考虑找到两个数中至少两个数的所有除数,然后保持其他数字可被整除的除数。这将消除对更大数量的除数的搜索。

(defn divisors [n]
  (into #{} (mapcat #(when (zero? (rem n %)) [% (/ n %)])
                    (range 1 (inc (Math/floor (Math/sqrt n)))))))

如果仍然无法通过测试,你应该寻找一些很好的公约数算法。

更新2

将更新的算法提交给hackerrank并且现在通过了