使用逐位运算优化Java数学
在我的Java类中,我必须使用Bailey–Borwein–Plouffe公式计算精确到小数点后15位的Pi值。在公式中,我需要计算16乘以n的幂(从1到50000的整数) 这就是我正在使用的公式 这是我的计算代码:使用逐位运算优化Java数学,java,Java,在我的Java类中,我必须使用Bailey–Borwein–Plouffe公式计算精确到小数点后15位的Pi值。在公式中,我需要计算16乘以n的幂(从1到50000的整数) 这就是我正在使用的公式 这是我的计算代码: double value = 0.0; //Calculates and increments value by using the BBP formula for(int i = 0; i < iterations; i++) { if(i == 0) {
double value = 0.0;
//Calculates and increments value by using the BBP formula
for(int i = 0; i < iterations; i++) {
if(i == 0) {
value += (1 / 1) * (
(4.0 / ((8 * i) + 1)) -
(2.0 / ((8 * i) + 4)) -
(1.0 / ((8 * i) + 5)) -
(1.0 / ((8 * i) + 6)) );
} else {
value += (1.0 / (2L<<(i<<2L))) * (
(4.0 / ((8 * i) + 1)) -
(2.0 / ((8 * i) + 4)) -
(1.0 / ((8 * i) + 5)) -
(1.0 / ((8 * i) + 6)) );
}
}
双值=0.0;
//使用BBP公式计算并增加值
对于(int i=0;i value+=(1.0/(2L可以表示为大于零的双精度的最小值是2-2048。对于上面(2048/4)的每一项,该公式都将以双精度的形式达到零,即512。增加到50000000太远了。您只需要15位数字吗?给您:
class Class {
private static final int ITERATION_COUNT = 15;
public static void main(final String... args) {
System.out.println(generatePi());
}
private static double generatePi() {
double pi = 0;
long sixteenPowK = 1;
for (int k = 0; k < ITERATION_COUNT; k++) {
pi += 1.0 / sixteenPowK * kthTerm(k);
sixteenPowK *= 16;
}
return pi;
}
private static double kthTerm(final int k) {
return 4.0 / (8.0 * k + 1)
- 2.0 / (8.0 * k + 4)
- 1.0 / (8.0 * k + 5)
- 1.0 / (8.0 * k + 6);
}
}
类{
私有静态最终整数迭代计数=15;
公共静态void main(最终字符串…参数){
System.out.println(generatePi());
}
专用静态双生成器{
双pi=0;
长十六周=1;
for(int k=0;k<迭代次数;k++){
pi+=1.0/十六周*kthTerm(k);
十六周*=16;
}
返回pi;
}
专用静态双kthTerm(最终整数k){
回报率4.0/(8.0*k+1)
-2.0/(8.0*k+4)
-1.0/(8.0*k+5)
-1.0/(8.0*k+6);
}
}
我很想看到一个微型基准免责声明:我远非数值方法方面的专家
一般来说,为了解决任何问题,我都避免预先优化。一旦我找到了解决方案,我就开始优化,只有在某种程度上需要的时候
在这种情况下,我将8*I
乘法内联到factor8
变量中,在循环中删除了if
,并计算了I=0
的初始值,最重要的是,我在乘法中累积了(1/16)^I
的值
通过这些更改,我成功地计算出PI值,仅在11次迭代中精确到15位小数。以下是代码:
public class Pi {
public static void main(String[] args) {
int iterations = 11;
int start = 1;
double value = 4.0 - 2.0 / 4.0 - 1.0 / 5.0 - 1.0 / 6.0;
double oneSixteenth = 1.0 / 16.0;
double oneSixteenthToN = oneSixteenth;
for (int i = start; i < iterations; i++) {
double factor8 = 8.0 * i;
value += oneSixteenthToN * (
(4.0 / (factor8 + 1)) -
(2.0 / (factor8 + 4)) -
(1.0 / (factor8 + 5)) -
(1.0 / (factor8 + 6)));
oneSixteenthToN *= oneSixteenth;
}
System.out.println("value = " + value); // our calculated value
System.out.println(" pi = " + Math.PI); // exact value
}
}
我必须承认,我不知道代码中累积错误的原因,但我几乎可以肯定这与(1/16)的计算有关^i
term.我已经解决了我自己的问题。事实证明,我根本不需要按位运算。由于程序创建了50万次迭代来计算Pi,我可以使用+=和*=运算来增加预定义变量。这是我的最终代码,它在0.2秒内以50万次迭代计算Pi
//Computes Pi by using the BBP formula
public double computePi(int iterations) //<=50 000 000
{
final double d = 1 / 16.0;
double a = 16.0;
double b = -8;
double pi = 0.0;
for(int k = 0; k < iterations; k++)
{
a *= d;
b += 8;
pi += a * (4.0 / (b + 1)
- 2.0 / (b + 4)
- 1.0 / (b + 5)
- 1.0 / (b + 6));
}
return pi;
}
//使用BBP公式计算Pi
公共双计算PI(整数迭代)//您不需要每次从头开始计算功耗。每个连续项都乘以一个系数,该系数是前一项的1/16。不能保证位移位操作会使您的程序更快。在您真正做到这一点之前,我建议您坚持纯数学并相信(JIT)编译器大多数现代CPU的乘法速度与它们的移位速度一样快。从我看来,唯一能让它更快的方法是并行化每个迭代。正如@ControlAltDel所说,你不能保证使用不同的数学运算会更快。不确定是否优化了它,但删除了(1/1)
完全没有坏处。实际上,只需计算0处的值,然后在1处开始i
就可以删除if语句。基本上int value=4-1/2-1/5-1/6;for(int i=1;i
。那么你有什么建议吗?我尝试使用Math.exp计算幂(Math.log(16)*i),但计算时间仍然太长。或者,将迭代次数设置为不超过大约520次,或者如果i>520
15次迭代可能不会给出15位数字,则将附加项设置为零(尽管由于每个项都小于前一项的1/16,我想它必须)。只要(kthTerm(k)==0.0)
所有这些方法都能以极快的编译时间完美地工作。但是,我的作业需要5000万次迭代。不过,多亏了你们,我确实重新思考了我的计算方法:)@Andrewmcguiness,我同意不能保证n次迭代将提供n个10位数的精度。但是在15位数的特定情况下,15次迭代就足够了。@ElectricFountainCo,50次迭代对于15位数来说是一个很大的浪费。但是,如果你想将pi计算到100万位数,请放心。看看java.math.BigDecimal
,它将帮助您完成繁重的工作。@Andreas这是本作业的重点。我们接到一项任务,我们必须尽可能优化它。我们班的一些学生在0.2秒内计算了50万次Pi。
//Computes Pi by using the BBP formula
public double computePi(int iterations) //<=50 000 000
{
final double d = 1 / 16.0;
double a = 16.0;
double b = -8;
double pi = 0.0;
for(int k = 0; k < iterations; k++)
{
a *= d;
b += 8;
pi += a * (4.0 / (b + 1)
- 2.0 / (b + 4)
- 1.0 / (b + 5)
- 1.0 / (b + 6));
}
return pi;
}