如何使用BBP公式直接计算π的第n位

使用BBP公式可以直接求得十六进制π的第n位而不需要计算前n位的数(讲道理,我认为是可以计算十进制的第n位的,毕竟其本身就能直接计算出十进制的π,但我没试),其优点在于可以进行分布式计算,即将一个耗时的运算拆分成若干个运算单元在不同的机器上进行并行运算,提高运算效率。同时也可以用来测试计算机的性能。以下公式将直接求出十进制的π:
BBP formula

其中参数k代表的是所求π的精度,例如:当您希望求出π的前13位的话您只需要将k替换成13即可。其计算出来的结果为3.141592653589793238154767...,得出来也是一个无理数,但只有前13位代表Π。

接下来是如何求得十六进制π的第n位。
首先,您需要知道一个知识点:十进制的小数如何转换为十六进制
例如,小数3.1415926转换为16进制为3.243f69a25b094,为了获取第一个16进制的小数,我们只需要取3.1415926的小数部分0.1415925乘以16即可得到2.2654816,结果的整数位数即为16十六进制小数转换后端结果,再取2.2654816的小数部分0.2654816乘以16即可得到第二个16进制小数位,依次类推,当整数部分位10、11、12等大于9的数时,使用a、b、c等代替即可。

所以,如果需要得到16进制π的第n个数,只需要求得16^n*π即可:
π的第n位
其中
π的第n位

计算时,将式子中的j替换为1、4、5、6然后分别代入上式即可。
式子中的k表示精度,d表示需要计算π的哪一位置

继续得到
计算π得第n位

这里有两点值得注意:

  1. 式子中的mod符号意为求模,即取16^(d-k)除以(8k+j)得余数,然后再除以(8k+j),这样做的目的是:在计算过程中就去除整数部分(因为我们只需要的是小数部分,整数部分可以直接去除,这在当数据变得非常大时可以极大提高计算效率)。
  2. 将求和部分拆分为两部分[0,d,正无穷]原因和第一点一样,这样的话后面的式子就一定是个小数。所以,真正在计算过程中一定要保证k>d
    然后将计算结果代入第一个式子中即可

最后,还需要注意的点是:代入之后求得的结果需要先去除整数部分然后再乘以16即可得到16进制π的第n位。并且,为了保证第一个式子中求出来的值不为负值,您可能需要在做第一个减法之前加上一个大于4的整数。

下面贴出java代码:
您需要首先在类中添加常量

    private static final BigDecimal ONE = BigDecimal.ONE;
    private static final BigDecimal TWO = new BigDecimal(2);
    private static final BigDecimal FOUR = new BigDecimal(4);
    private static final BigDecimal FIVE = new BigDecimal(5);
    private static final BigDecimal SIX = new BigDecimal(6);
    private static final BigDecimal EIGHT = new BigDecimal(8);
    private static final BigDecimal SIXTEEN = new BigDecimal(16);

以下为方法实现代码,其中calc16dPI即表示计算(16^d)*πcalc16dSj即表示计算(16^d)*Sj,可以看到,我将k(即代码中的ACCURACY)设置为了d+10,这个数是我随便设置的,你也可以将其设置成其他的,具体影响大不大我还没研究。

    BigDecimal calc16dPI(int d) {
        return FOUR.multiply(calc16dSj(d, 1)).add(BigDecimal.valueOf(3)).subtract(TWO.multiply(calc16dSj(d, 4)).divideAndRemainder(ONE)[1]).subtract(calc16dSj(d, 5).divideAndRemainder(ONE)[1]).subtract(calc16dSj(d, 6).divideAndRemainder(ONE)[1]).divideAndRemainder(ONE)[1];
    }

    BigDecimal calc16dSj(int d, int j) {
        int ACCURACY = d + 10;
        BigDecimal part1 = BigDecimal.ZERO;
        BigDecimal part2 = BigDecimal.ZERO;
        for (int k = 0; k <= d; k++) {
            part1 = part1.add(SIXTEEN.pow(d - k).divideAndRemainder(EIGHT.multiply(BigDecimal.valueOf(k)).add(BigDecimal.valueOf(j)))[1].divide(EIGHT.multiply(BigDecimal.valueOf(k)).add(BigDecimal.valueOf(j)), ACCURACY, BigDecimal.ROUND_HALF_UP));
        }

        for (int k = d + 1; k < ACCURACY; k++) {
            part2 = part2.add(ONE.divide(SIXTEEN.pow(k - d).multiply(EIGHT.multiply(BigDecimal.valueOf(k)).add(BigDecimal.valueOf(j))), ACCURACY, BigDecimal.ROUND_HALF_UP));
        }

        return part1.add(part2);
    }

以下为测试方法,计算十六进制π的前100位,注意在结果上乘以一个16

    @Test
    public void mainCalc() {
        for (int d = 0; d < 100; d++) {
            System.out.println("index of " + (d + 1) + ": " + calc16dPI(d).multiply(SIXTEEN));
        }
    }

运行结果截图:
运行结果

分别得到2、4、3、15、6、10、8、8、8、5...则π的十六进制数应该为3.243f6a8885...(整数位3是我手动加上去的)
参考 我有点酷
参考 维基百科The BBP Algorithm for Pi
如果您有什么需要补充或者我说错的地方欢迎发邮件给我 zouheng613@163.com
bingo

  • 9
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值