Baking-Pi挑战 - 理解&提高

时间:2014-03-10 03:20:06

标签: math clojure clojurescript reddit pi

我昨天花了一些时间为this challenge published on Reddit编写解决方案,并且能够在没有作弊的情况下完成它,但我还有几个问题。 Reference material here

这是我的代码。

(ns baking-pi.core
  (:import java.math.MathContext))

(defn modpow [n e m]
  (.modPow (biginteger n) (biginteger e) (biginteger m)))

(defn div [top bot]
  (with-precision 34 :rounding HALF_EVEN 
    (/ (bigdec top) (bigdec bot))))

(defn pow [n e]
  (.pow (bigdec n) (bigdec e) MathContext/DECIMAL128))

(defn round
  ([n] (.round (bigdec n) MathContext/DECIMAL128))
  ([n & args] (->> [n args] (flatten) (map round))))

(defn left [n d]
  (letfn [(calc [k] (let [bot (+' (*' 8 k) d)
                          top (modpow 16 (-' n k) bot)]
                      (div top bot)))]
    (->> (inc' n)
         (range 0)
         (map calc)
         (reduce +'))))

(defn right [n d]
  (letfn [(calc [[sum'' sum' k]]
                (let [sum' (if (nil? sum') 0M sum')
                      top (pow 16 (-' n k))
                      bot (+' (*' k 8) d)
                      delta (div top bot)]
                  [sum' (+' sum' delta) (inc' k)]))
          (pred [[sum'' sum' k]]
                (cond (or (nil? sum'') (nil? sum')) true
                      (apply == (round sum'' sum')) false
                      :else true))]
    (->> [nil nil (inc' n)]
         (iterate calc)
         (drop-while pred)
         (first)
         (second))))

(defn bbp [n]
  (letfn [(part [m d] (*' m (+' (left n d) (right n d))))]
    (let [sum (-' (part 4 1) (part 2 4) (part 1 5) (part 1 6))]
      (-> sum
          (-' (long sum))
          (*' 16)
          (mod 16)
          (Long/toHexString)))))

我有两个问题。

  1. wiki发表以下声明。由于我的计算精确到十进制后的34位数,我如何利用它来生成每个bbp调用的更多十六进制数字?

      理论上,接下来的几位数字可达到计算的准确性   使用也准确

  2. 我的算法依赖于BigInteger的modPow进行模幂运算(基于以下引用),以及其他地方的BigDecimals。它也很慢。请记住,我不想在每个问题#1中失去有意义的准确性,加速这个程序并使其成为有效的clojurescript以及clojure的最佳方法是什么?

      

    要快速有效地计算16 n - k mod(8k + 1),请使用   模幂运算算法。

  3. 编辑:从3个问题改为2个。管理自己回答第一个问题。

1 个答案:

答案 0 :(得分:2)

  1. 如果您希望每个bpp调用计算更多位

    然后你必须将方程从1/(16^k)基数改为大基数。你可以通过总结2次迭代(kk+1)来做到这一点,所以你有类似的东西

    (...)/16^k  + (...)/16^(k+1)
    (...)/256^k
    

    但在这种情况下,您需要更精确的int操作。使用不太精确的迭代通常会更快

  2. 如果您查看基本等式,那么您会发现根本不需要bigint进行计算

    这就是使用此迭代的原因,但输出数当然是bigint。因此,您无需在bigint上计算模块化算术。

    我不知道您使用的那个是多么优化......但这是我的:

  3. 如果您只想要速度而不是无限精度,那么请使用其他PSLQ方程式

    我对PSLQ的理解是找到实数和整数迭代之间关系的算法。

  4. 这是我最喜欢的800 digits of Pi algorithm,如果链接出现故障,我会从中提取代码:

    //The following 160 character C program, written by Dik T. Winter at CWI, computes pi  to 800 decimal digits. 
    int a=10000,b,c=2800,d,e,f[2801],g;main(){for(;b-c;)f[b++]=a/5;
    for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);}