使用BBP公式可以直接求得十六进制π的第n位而不需要计算前n位的数(讲道理,我认为是可以计算十进制的第n位的,毕竟其本身就能直接计算出十进制的π,但我没试),其优点在于可以进行分布式计算,即将一个耗时的运算拆分成若干个运算单元在不同的机器上进行并行运算,提高运算效率。同时也可以用来测试计算机的性能。以下公式将直接求出十进制的π:
其中参数
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*π即可:
其中
计算时,将式子中的
j
替换为1、4、5、6然后分别代入上式即可。
式子中的k
表示精度,d
表示需要计算π的哪一位置
继续得到
这里有两点值得注意:
- 式子中的
mod
符号意为求模,即取16^(d-k)除以(8k+j)得余数,然后再除以(8k+j),这样做的目的是:在计算过程中就去除整数部分(因为我们只需要的是小数部分,整数部分可以直接去除,这在当数据变得非常大时可以极大提高计算效率)。- 将求和部分拆分为两部分[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
:
@Testpublic 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