我正在编写一个实现以下表达式的函数(1 / n!)*(1!+2!+3!+ ... + n!)。
该函数传递给了参数n
,我必须将上述语句作为double返回,截断到第6个小数位。我遇到的问题是因子值变得如此之大以至于它变为无穷大(对于n
的大值)。
这是我的代码:
public static double going(int n) {
double factorial = 1.00;
double result = 0.00, sum = 0.00;
for(int i=1; i<n+1; i++){
factorial *= i;
sum += factorial;
}
//Truncate decimals to 6 places
result = (1/factorial)*(sum);
long truncate = (long)Math.pow(10,6);
result = result * truncate;
long value = (long) result;
return (double) value / truncate;
}
现在,上面的代码适用于n = 5或n = 113,但是n = 170以上,factorial
和sum
表达式变为无穷大。由于数字的指数增长,我的方法是不会起作用的?那么计算非常大的数字并不会对性能产生太大的影响会产生什么影响(我相信BigInteger在查看类似的问题时相当慢)。
答案 0 :(得分:3)
您可以在不评估单个因子的情况下解决此问题。
您的公式简化为相当简单的计算方式
1!/n! + 2!/n! + 3!/n! + ... + 1
除了第一个和最后一个术语之外,很多因素实际上都会取消,这将有助于最终结果的精确度,例如3! / n!
只需将1 / 4
乘以{1 / n
1}}。你不能做的是评估阶乘并将它们分开。
如果精度的15位十进制数字是可接受的(看起来它来自您的问题),那么您可以在浮点中对此进行评估,首先添加小项。在开发算法时,您会注意到这些术语是相关的,但是在您冒险引入材料不精确的情况下,如何利用这些术语会非常小心。 (如果我是你,我会认为这是第二步。)
这是一个原型实现。请注意,我首先在数组中累积所有单个术语,然后我首先从较小的术语开始总结它们。我认为从最后一个术语(1.0)开始计算更准确并向后工作,但对于收敛速度如此之快的系列而言,这可能不是必需的。让我们彻底完成并分析结果。
private static double evaluate(int n){
double terms[] = new double[n];
double term = 1.0;
terms[n - 1] = term;
while (n > 1){
terms[n - 2] = term /= n;
--n;
}
double sum = 0.0;
for (double t : terms){
sum += t;
}
return sum;
}
你可以看到第一个术语变得微不足道的速度有多快。我认为您只需要几个术语来计算浮点double
的容差的结果。让我们设计一个算法,以便在达到该点时停止:
最终版。系列似乎收敛得如此之快,以至于您不必担心首先添加小术语。所以你最终得到了绝对美丽
private static double evaluate_fast(int n){
double sum = 1.0;
double term = 1.0;
while (n > 1){
double old_sum = sum;
sum += term /= n--;
if (sum == old_sum){
// precision exhausted for the type
break;
}
}
return sum;
}
正如您所看到的,不需要BigDecimal
&amp; c,当然也不需要评估任何因子。
答案 1 :(得分:0)
您可以像这样使用BigDecimal:
public static double going(int n) {
BigDecimal factorial = BigDecimal.ONE;
BigDecimal sum = BigDecimal.ZERO;
BigDecimal result;
for(int i=1; i<n+1; i++){
factorial = factorial.multiply(new BigDecimal(i));
sum = sum.add(factorial);
}
//Truncate decimals to 6 places
result = sum.divide(factorial, 6, RoundingMode.HALF_EVEN);
return result.doubleValue();
}