代码之家  ›  专栏  ›  技术社区  ›  Exit

用牛顿法重建Excel速率函数

  •  6
  • Exit  · 技术社区  · 14 年前

    我正在用PHP转换抵押贷款计算器,但不一定需要PHP解决方案。我正在寻找复制Excel所需的逻辑 RATE 功能。我找到了一个使用二分法的解决方案,如果更糟的话,我会用它。

    我知道有人在互联网世界有这样一个功能的知识,所以我希望有一个简单的答案,而不是从头开始创建一个解决方案。

    参考文献:

    谢谢

    2 回复  |  直到 7 年前
        1
  •  8
  •   Mark Baker    14 年前

    使用取自phpexcel的割线法(牛顿法的有限差分近似)实现ms excel rate()函数:

    define('FINANCIAL_MAX_ITERATIONS', 128);
    define('FINANCIAL_PRECISION', 1.0e-08);
    
    
    function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) {
    
        $rate = $guess;
        if (abs($rate) < FINANCIAL_PRECISION) {
            $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
        } else {
            $f = exp($nper * log(1 + $rate));
            $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
        }
        $y0 = $pv + $pmt * $nper + $fv;
        $y1 = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
    
        // find root by secant method
        $i  = $x0 = 0.0;
        $x1 = $rate;
        while ((abs($y0 - $y1) > FINANCIAL_PRECISION) && ($i < FINANCIAL_MAX_ITERATIONS)) {
            $rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0);
            $x0 = $x1;
            $x1 = $rate;
    
            if (abs($rate) < FINANCIAL_PRECISION) {
                $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
            } else {
                $f = exp($nper * log(1 + $rate));
                $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
            }
    
            $y0 = $y1;
            $y1 = $y;
            ++$i;
        }
        return $rate;
    }   //  function RATE()
    
        2
  •  0
  •   chuckedw    7 年前

    我尝试使用上面的代码,但结果与Excel(或谷歌电子表格)完全不同。

    我不知道您是否需要实现这个函数,但无论如何,我看了这个算法是如何构建的,即使我无法访问Excel源代码(或谷歌工作表),我发现这不是一个简单的计算。关于这个数学,可以在这里阅读更多:

    https://brownmath.com/bsci/loan.htm#Eq8

    在PHP中,函数可能如下所示:

    function rate($nprest, $vlrparc, $vp, $guess = 0.25) {
        $maxit = 100;
        $precision = 14;
        $guess = round($guess,$precision);
        for ($i=0 ; $i<$maxit ; $i++) {
            $divdnd = $vlrparc - ( $vlrparc * (pow(1 + $guess , -$nprest)) ) - ($vp * $guess);
            $divisor = $nprest * $vlrparc * pow(1 + $guess , (-$nprest - 1)) - $vp;
            $newguess = $guess - ( $divdnd / $divisor );
            $newguess = round($newguess, $precision);
            if ($newguess == $guess) {
                return $newguess;
            } else {
                $guess = $newguess;
            }
        }
        return null;
    }