我需要找一个数字(我的猜测),当在一个简单的计算中使用时会产生一个特定的总和(目标)。为此,我编写了一个calcGuess函数。它有效,但我想减少计算猜测所需的迭代次数。
我的方法是在猜测到达迭代时添加或减去修饰符。
如果上一次迭代的猜测高于目标数,那么我将修改器分成两部分并从我的猜测中减去它。
如果上一次迭代的猜测低于目标数,那么我将修改器分成两部分并将其添加到我的猜测中。
如果前一次迭代没有超过或低于目标数,但让我更接近目标数,那么我将继续使用修改器而不更改它。
任何提高猜测算法效率的建议都将受到高度赞赏。
var _target = 40000;
var _cashflow = [1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1398];
var _guess = 0.0014534646484;
var _modifier = 0.01;
var correctGuess = calcGuess(_target, _cashflow, _guess, _modifier, false, false, false, 0);
function calcGuess(target, cashflow, guess, modifier, hasPrevGuess, prevGuessToHigh, prevGuessToLow, iterations) {
// prevent an infinite loop
if (++iterations > 100) {
console.log('Kill switch');
return;
}
// calculate a new target using the cashflow array and our guess
var targetUsingGuess = 0;
for (var n = 0; n < cashflow.length; n++) {
targetUsingGuess += cashflow[n] / Math.pow(1 + guess, n);
}
// compare our new target to the original target
if (targetUsingGuess.toFixed(8) == target.toFixed(8)) {
// the guess matched!
console.log(iterations, ' Matched!: ', guess);
return guess;
}
else {
// the guess was too high
if (targetUsingGuess > target) {
// this where I modify my guess and
// this is part which I think can be much efficient
if (hasPrevGuess && prevGuessToLow) {
modifier /= 2;
}
guess += modifier;
console.log(iterations, ' Too high: ', guess);
return calcGuess(target, cashflow, guess, modifier, true, true, false, iterations);
}
// the guess was too low
if (targetUsingGuess < target) {
// this where I modify my guess and
// this is part which I think can be much efficient
if (hasPrevGuess && prevGuessToHigh) {
modifier /= 2;
}
guess -= modifier;
console.log(iterations, ' Too low: ', guess);
return calcGuess(target, cashflow, guess, modifier, true, false, true, iterations);
}
}
}
答案 0 :(得分:2)
由于我们有结果并希望找到您获得结果的函数的输入,Newton-Raphson method of finding roots将是最佳选择。
您必需的目标T
必须从t(x) = sum of C[i] / (1 + x)^i
函数计算,其中0 <= i < n
。 C
代表您的cashflow
数组,n
代表cashflow.length
,x
代表您的猜测。该函数可以重写如下:t(x) = sum of C[i] * (1 + x)^(-i)
。如果所有C[i]
相等,则可以使用几何系列的总和将函数简化为有理函数,但由于您有现金流数组,我将假设它们全部是不同的,并保持原样t(x)
。
您的问题现在缩小为查找g
的{{1}}(猜测)。
注意:对于给定的函数,明显的垂直渐近线为t(g) = T
。由于总和的性质,对于任何x = -1
(现金流数组中的第一个元素),T > C[0]
。我将使用这个假设,即期望的目标总是大于现金流阵列的第一个元素。给定的方法也适用于g > -1
,初始猜测稍有变化,但如果T < C[0]
小于或等于奇数{{的函数的最小值,则最终会导致除以零。 1}}。 T
保证n
是单调的,但f(x)
中的单调性仅保证x > -1
。
Newton-Raphson寻找根的方法的工作算法如下:
如果x < -1
是n
猜测,
x(i)
其中i
是x(i+1) = x(i) - f(x(i)) / f'(x(i))
的衍生物。
在我们的情况下,f'(x)
,因为我们必须找到f(x)
的{{1}}的值。
根据我们对f(x) = t(x) - T
,x
的定义。
唯一剩下的就是找到一个好的初步猜测。函数的性质与我们的假设相结合,使我们能够对这个问题给出一个非常简单的答案。由于t(x) = T => t(x) - T = 0 => f(x) = 0
的函数正在递减,我们只需取最小数f(x)
。从您的代码看,您的容错(f'(x) = t'(x) = sum of C[i] * (-i) * (1 + x)^(-i - 1)
)似乎是x > -1
(前8个十进制数字必须相同)。因此,初始猜测为-1
。
你可能会争辩说,由于函数对于TOL
是单调的,我们可以选择任何1e-8
作为初始猜测,但由于函数的不连续性,猜测大于特定值取决于在函数定义上将导致下一个猜测小于-1 + TOL
,随后的猜测将发散到无穷大而不是收敛。
编辑:经过一些测试后,x > -1
进行了更好的初始猜测,在10到20次迭代中收敛,相对于x > -1
在~500次迭代中的收敛。
当两个猜测之间的绝对差值小于-1
时,算法必须终止,但是如果目标和猜测结果之间的绝对差值(简称1 - target / sum(cashflow)
)小于-1 + TOL
因为某些情况下的收敛速度可能非常慢。
TOL
&#13;