【问题标题】:Baking-Pi Challenge - Understanding & ImprovingBaking-Pi 挑战 - 理解和改进
【发布时间】:2014-04-13 01:05:34
【问题描述】:

我昨天花了一些时间为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)))))

我有 2 个问题。

  1. 维基做出以下声明。由于我的计算精确到小数点后 34 位,我如何利用它为每个 bbp 调用生成更多的 PI 十六进制数字?

    理论上,接下来的几位数字取决于计算的准确性 使用也将是准确的

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

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

编辑:从 3 个问题更改为 2 个问题。设法自己回答了第一个问题。

【问题讨论】:

    标签: math clojure clojurescript reddit pi


    【解决方案1】:
    1. 如果您希望每次 bpp 调用计算更多位

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

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

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

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

      这就是为什么使用这种迭代但输出数字当然是bigint。所以你不需要在bigint上计算模运算。

      我不知道您使用的优化程度如何……但这是我的:

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

      我对@9​​87654322@的理解是,它是一种寻找实数和整数迭代之间关系的算法。

    这是我最喜欢的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);}
    

    【讨论】:

    • 将两个运算相加得到的公分母为 16^(k+1)=16*16^k,而不是 32^k=(2^k)*(16^k)。
    • 幂比乘法结合更强,16*16^k 是 16*(16^k) 而不是 (16*16)^k。当然,如果将 k 或 k+1 到 2k 的项相加,那么公分母是 16^(2k)=256^k。
    • 16^(2*k) = 256^k16^(2*k+1) = 16*(256^k)。因此,利用连续项的奇数/偶数,您可以得到一个带有256^n 分母的表达式。
    • AFAIK,您附上的 C 代码根本不是 PSLQ 算法。父文章在讨论不同的方法,最后附上简短的 C 程序只是因为它是一个很酷的程序,而不是因为它是 PSLQ。我可以肯定,因为I've just reimplementated it。该代码在 32 位定点算术中使用了老式的 arctan() 系列,每次迭代计算 4 个十进制数字,请参阅crypto.stanford.edu/pbc/notes/pi/code.html
    猜你喜欢
    • 2019-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多