Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Clojure newbie here, trying to get my head around it. Obviously there are better ways of obtaining pi, but for educational purposes, how is the code below?

(defn leibniz-numerator [x] (* (- (rem x 4) 2) -4.0))

(defn calc-pi-leibniz
    "Calculate pi with Leibniz formula
    4/1 - 4/3 + 4/5 - 4/7 + 4/9 etc
    Very slow convergence"
    [terms]
    (reduce + (map #(/ (leibniz-numerator %) %) (range 1 terms 2))))

(print (calc-pi-leibniz 10000000))
share|improve this question
add comment

1 Answer

I'd say your solution is pretty good! Since you probably won't re-use your leibniz-numerator function, you might want to make it private by declaring it with defn-, or you could declare it within the body of your main function using let or letfn.

Also, you might notice that your function actually computes pi with half the number of terms given has parameters (might not be of importance).

I can share some alternatives I came up with.

We could start by generating the sequence (4 -4 4 -4...). To do that, we can use the method iterate:

(take 4 (iterate - 4))
;;=> (4 -4 4 -4)

Then we want the sequence (1 3 5 7). You already found method rangefor that:

(take 4 (range 1 10 2))
;;=> (1 3 5 7)

If you divide those to element wise, you will get (4 -4/3 4/5 -4/7...). You can do that with map

(take 4 (map / (iterate - 4) (range 1 10 2)))
;;=> (4 -4/3 4/5 -4/7)

In our final computation, this can get really slow because we're using decimals. Let's use floats instead, and put everything together:

(defn calc-pi-leibniz [terms]
  (reduce + (map / (iterate - 4.0) (range 1.0 terms 2.0))))

But for some reason, this is 5 times slower than your solution. If someone could tell me why, I'd be grateful. I suspect that is because map has to go through two lists instead of one, but since they are both lazy this should not matter?

For a faster solution (but less elegant), we can see that there are two subsequences in the leibniz formula. One that is (4 4/5 4/9) and one that is (-4/3 -4/7 -4/11). With that in mind, you can get the following:

(defn calc-pi-leibniz [terms]
  (* 4 (- (reduce + (map / (range 1.0 terms 4.0)))
          (reduce + (map / (range 3.0 terms 4.0))))))

Which runs as fast as your solution.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.