加速Clojure以避免超时
我做了一些hackerrank挑战,并注意到我似乎无法编写高效的代码,因为我经常超时,即使通过测试的答案是正确的。例如,这是我的代码:加速Clojure以避免超时,clojure,Clojure,我做了一些hackerrank挑战,并注意到我似乎无法编写高效的代码,因为我经常超时,即使通过测试的答案是正确的。例如,这是我的代码: (let [divisors (fn [n] (into #{n} (into #{1} (filter (comp zero? (partial rem n)) (range 1 n))))) str->ints (fn [string] (map #(Integer/parseInt %)
(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不存在。为了简洁起见,我把它放在这里。在这种情况下,map
功能明显被误用:
尽管clojure集合是懒惰的,但对它们的操作仍然不是免费的。因此,当你链接大量的map
s时,你仍然拥有所有的中间集合(这里有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..n(sqrt n)的范围开始,当x
是n
的除数时,将x
和n/x
添加到结果集中,这将为大数带来相当大的利润:
(defn divisors [n]
(into #{} (mapcat #(when (zero? (rem n %)) [% (/ n %)])
(range 1 (inc (Math/floor (Math/sqrt n)))))))
我也会考虑找出所有两个数中最小的除数,然后把另一个数除以。这将消除对较大数的除数的搜索
(defn common-divisors [pair]
(let [[a b] (sort pair)
divs (divisors a)]
(filter #(zero? (rem b %)) divs)))
如果仍然无法通过测试,您可能应该寻找一些不错的公约数算法
(defn common-divisors [pair]
(let [[a b] (sort pair)
divs (divisors a)]
(filter #(zero? (rem b %)) divs)))
更新2
将更新后的算法提交给hackerrank,现在通过得很好在这种情况下,
map
功能存在明显的误用:
尽管clojure集合是懒惰的,但对它们的操作仍然不是免费的。因此,当你链接大量的map
s时,你仍然拥有所有的中间集合(这里有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..n(sqrt n)的范围开始,当x
是n
的除数时,将x
和n/x
添加到结果集中,这将为大数带来相当大的利润:
(defn divisors [n]
(into #{} (mapcat #(when (zero? (rem n %)) [% (/ n %)])
(range 1 (inc (Math/floor (Math/sqrt n)))))))
我也会考虑找出所有两个数中最小的除数,然后把另一个数除以。这将消除对较大数的除数的搜索
(defn common-divisors [pair]
(let [[a b] (sort pair)
divs (divisors a)]
(filter #(zero? (rem b %)) divs)))
如果仍然无法通过测试,您可能应该寻找一些不错的公约数算法
(defn common-divisors [pair]
(let [[a b] (sort pair)
divs (divisors a)]
(filter #(zero? (rem b %)) divs)))
更新2
将更新后的算法提交给hackerrank,现在它通过得很好有趣的是,即使有了这些更改,我仍然会在测试用例7、8、9和10上计时。因此,我的代码肯定还有其他一些性能差的问题。这可能是因为低效的除数搜索算法。我会首先尝试通过将搜索数量限制在
(sqrt n)
来优化它。有趣的是,即使有了这些更改,我仍然会在测试用例7、8、9和10上超时。因此,我的代码肯定还有其他一些性能差的问题。这可能是因为低效的除数搜索算法。我将首先尝试通过将搜索数量限制为(sqrt n)
来优化它。