2015-10-12 7 views
7

を減らすねじ切りマップを最適化:それは数字が大きい得るとき、それは時間が爆発を実行していることを除いて、うまく働いているClojureの - 私は次のコードを持っている

(defn series-sum 
    "Compute a series : (+ 1 1/4 1/7 1/10 1/13 1/16 ...)" 
    [n] 
    (->> (iterate (partial + 3) 1) 
     (map #(/ 1 %)) 
     (take n) 
     (reduce +) 
     float 
     (format "%.2f") 
     (str))) 

を。私のコンピュータで(series-sum 2500)はおそらく2番目または2つですが、(series-sum 25000)と私は私のREPLを殺す必要があります。

可能な限り(take n)を移動しようとしましたが、それでは不十分です。なぜ私はそれが遅くなるのか分からないので、私はClojureについて何かを理解していないと感じている(私は(series-sum 25000)(series-sum 2500)と約10倍かかると思う)。

最適化するための明白なループ/反復解がありますが、ステップを印刷して1つのステップ(ドキュメントストリングのように見える(take n))を持つことができるという考えが好きです。

デバッグ性を維持しながらこのコードのパフォーマンスを向上させるにはどうすればよいですか?

さらに、各ステップの時間を測定して、時間を測定することはできますか?

+2

関連性:http://stackoverflow.com/q/26954404/251311それを楽しむための – zerkms

答えて

8

はい、@ zerkmsのリンクに関係します。あなたは、おそらくより良いフロートにマップする必要があり、有理数にマップ:

(defn series-sum 
    "Compute a series : (+ 1 1/4 1/7 1/10 1/13 1/16 ...)" 
    [n] 
    (->> (iterate (partial + 3) 1) 
     (take n) 
     (map #(/ 1.0 %)) 
     (reduce +) 
     (format "%.2f"))) 

は、今でははるかに高速に動作します:

user> (time (series-sum 2500000)) 
"Elapsed time: 686.233199 msecs" 
"5,95" 
+2

トランスデューサのバージョン:https://www.refheap.com/110572 – cfrick

+0

Ah shame: rationalsとしてのシーケンス.. @cfrickは、本当にいいです、ありがとう。 – nha

6

を数学的な操作のこのタイプの場合は、ループ内のコンピューティングは、怠惰なシーケンスを使用するよりも高速です。これは、より高速な私のために他の答えよりも桁違いです:

(defn series-sum 
    [n] 
    (loop [i 0 
     acc 0.0] 
    (if (< i n) 
     (recur (inc i) 
      (+ acc (/ (float 1) (inc (* 3 i))))) 
     (format "%.2f" acc)))) 

注:formatは文字列を返すので、あなたはstrは必要ありません。

編集:もちろん、これは元の質問のコードの主な問題ではありません。改善の大部分は、他の答えに示されているように、有理数を排除することから来ています。これは単なる最適化です。

+1

明らかにループは速いですが、それが主な問題だと確信していますか?私は、主な問題は、各乗算に時間がかかる原因となる合理化の規模が大きくなることを期待していました。ソリューションは高速ですが、ループを使用するだけでなく浮動小数点にも切り替えるため、パフォーマンスの主な改善点はどれか分かりません。 – amalloy

+1

はい、私は、switch-to-floatの答えよりもはるかに速いと言っていました。これは、すでに低い数千のnに対してOPの有理数よりも約3桁速いものでした。 –

関連する問題