2015-10-12 33 views
7

ho il seguente codice:Clojure - ottimizzare una mappa filettato ridurre

(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))) 

Si sta lavorando bene, salvo che in esecuzione esplode tempo quando i numeri diventano grandi. Sul mio computer (series-sum 2500) è forse un secondo o due, ma (series-sum 25000) e devo uccidere il mio REPL.

Ho provato a spostare (take n) il più lontano possibile, ma ciò non è sufficiente. Sento che non capisco qualcosa su Clojure poiché non vedo perché sarebbe più lento (mi aspetterei che lo (series-sum 25000) richieda circa 10 volte come (series-sum 2500)).

C'è una soluzione loop/recur evidente per ottimizzarlo, ma mi piace l'idea di poter stampare i passaggi e di avere un passo (il (take n) sembra come la docstring).

Come posso migliorare le prestazioni di questo codice mantenendo la debugabilità?

Ancora meglio, posso misurare il tempo di ogni passaggio per vedere quello che richiede tempo?

+2

Relevant: http://stackoverflow.com/q/26954404/251311 – zerkms

risposta

8

sì, è rilevante per il collegamento di @ zerkms. Si mappa di razionali, probabilmente dovrebbe meglio mappare i galleggianti:

(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"))) 

ora funziona molto più veloce:

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

per il divertimento di esso: la versione trasduttore: https://www.refheap.com/110572 – cfrick

+0

Ah, vergogna: mi è piaciuto essere in grado di stampare la sequenza come razionali .. @cfrick che è davvero bella, grazie. – nha

6

Per questo tipo di operazione matematica, l'informatica in un ciclo è più veloce rispetto all'utilizzo di sequenze pigri. Questo è un ordine di grandezza più veloce rispetto agli altri risposta per me:

(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)))) 

Nota: non è necessario il str perchè format restituisce una stringa.

Modifica: ovviamente questo non è il problema principale con il codice nella domanda originale. La maggior parte del miglioramento deriva dall'eliminazione dei razionali come mostrato dall'altra risposta. Questa è solo un'ulteriore ottimizzazione.

+1

Ovviamente il ciclo è più veloce, ma sei sicuro che sia il problema principale? Mi aspettavo che il problema principale fosse la crescente dimensione dei razionali che causava un prolungamento di ogni moltiplicazione. La tua soluzione è più veloce, ma si passa ai float e all'utilizzo di un loop, rendendo difficile sapere qual è il miglioramento delle prestazioni principale. – amalloy

+1

Sì, intendevo un ordine di grandezza più veloce della risposta switch-to-float, che era già di circa tre ordini di grandezza più veloce dei razionali di OP per n nelle poche migliaia. –