优化我的猜测算法

时间:2017-10-28 20:44:54

标签: javascript algorithm math

我需要找一个数字(我的猜测),当在一个简单的计算中使用时会产生一个特定的总和(目标)。为此,我编写了一个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);
    }
  }
}

1 个答案:

答案 0 :(得分:2)

由于我们有结果并希望找到您获得结果的函数的输入,Newton-Raphson method of finding roots将是最佳选择。

您必需的目标T必须从t(x) = sum of C[i] / (1 + x)^i函数计算,其中0 <= i < nC代表您的cashflow数组,n代表cashflow.lengthx代表您的猜测。该函数可以重写如下: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 < -1n猜测,

x(i)

其中ix(i+1) = x(i) - f(x(i)) / f'(x(i)) 的衍生物。

在我们的情况下,f'(x),因为我们必须找到f(x)的{​​{1}}的值。

根据我们对f(x) = t(x) - Tx的定义。

唯一剩下的就是找到一个好的初步猜测。函数的性质与我们的假设相结合,使我们能够对这个问题给出一个非常简单的答案。由于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因为某些情况下的收敛速度可能非常慢。

&#13;
&#13;
TOL
&#13;
&#13;
&#13;