【问题标题】:Recreate Excel RATE function using Newton's Method使用牛顿法重新创建 Excel RATE 函数
【发布时间】:2011-03-13 01:16:37
【问题描述】:

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

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

参考资料:

谢谢

【问题讨论】:

    标签: php excel function financial


    【解决方案1】:

    使用 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()
    

    【讨论】:

    • 完美完美!只要记住人们为 FINANCIAL_PRECISION 和 FINANCIAL_MAX_ITERATIONS 插入一些 DEFINE 语句 - 或者 - 分别用这些静态值 0.0000001 和 20 替换它们。我在上面提到的 MS 网站上找到了这些值。
    • 这个方法有收敛问题,见stackoverflow.com/questions/14031208/…
    • @Exit - 那么也许您愿意提供更好的解决方案,分享您的知识将使我们所有人受益
    • @Exit - 道歉
    【解决方案2】:

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

    我不知道你是否需要实现这个功能,但无论如何,我查看了这个算法是如何构建的,即使我无法访问 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;
    }
    

    【讨论】:

      【解决方案3】:

      对于 Laravel 使用相同的功能,但你删除了定义

      define('FINANCIAL_MAX_ITERATIONS', 128);
      define('FINANCIAL_PRECISION', 1.0e-08);
      
      

      并且金融最大迭代次数 = 20; -> 相同的excel

      代码是:

      function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) {
          $financial_max_iterations = 20;
          $financial_precision = 0.00000008;
      
          $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;
      }  
      
      

      对我有用

      【讨论】:

        【解决方案4】:

        TL;DR:这是一个 SQL Server 版本。它不适用于某些值,上面的 PHP 代码可能会因相同的值而失败。

        长答案:我需要 SQL Server 的 RATE 函数。使用上面的 PHPExcel 答案,并使用 https://charlottecredittechnology.blogspot.com/2013/05/sql-2008-excel-like-rate-function-part.html 我写了一个 SQL Server 标量函数:

        ALTER function [dbo].[Rate](
          @nper integer, @pmt float, @pv float, @fv float, @type bit = 0, @guess float = 0.1
        ) returns numeric(38,10) as
        /*
        Calculate the effective interest rate of a sequence of regular payments.
        */
        begin    
          declare @returns numeric(38,10) = 0;
          if @type is null set @type = 0;
        
          declare @i integer;
          declare @rate float = @guess;
          declare @FINANCIAL_MAX_ITERATIONS integer = 100;
          declare @FINANCIAL_PRECISION float = 0.0000001;
          declare @y float, @y0 float, @y1 float, @f float, @x0 float, @x1 float;
        
          set @rate = @guess;
          if Abs(@rate) < @FINANCIAL_PRECISION
          begin
            set @f = 0;
            set @y = @pv * (1+@nper*@rate) + @pmt * (1+@rate*@type) * @nper + @fv;
          end
          else
          begin
            set @f = Exp(@nper * Log(1+@rate));
            set @y = @pv * @f + @pmt * (1/@rate + @type) * (@f-1) + @fv;
          end;
          set @y0 = @pv + @pmt * @nper + @fv;
          set @y1 = @pv * @f + @pmt * (1/@rate + @type) * (@f-1) + @fv;
        
          -- Newton secant method.
          set @i = 0;
          set @x0 = 0;
          set @x1 = @rate;
          while Abs(@y0-@y1) > @FINANCIAL_PRECISION and @i < @FINANCIAL_MAX_ITERATIONS
          begin
            set @rate = (@y1 * @x0 - @y0 * @x1) / (@y1-@y0);
            set @x0 = @x1;
            set @x1 = @rate;
            if Abs(@rate) < @FINANCIAL_PRECISION
            begin
              set @y = @pv * (1+@nper*@rate) + @pmt * (1+@rate*@type) * @nper + @fv;
            end
            else
            begin
              set @f = Exp(@nper * Log(1+@rate));
              set @y = @pv * @f + @pmt * (1/@rate + @type) * (@f-1) + @fv;
            end;
            set @y0 = @y1;
            set @y1 = @y;
            set @i = @i + 1;
          end;
          return Convert(numeric(38,10), @rate);
        end;
        

        不幸的是,它并不总是有效。以下是我汇总并使用 Excel 检查的一些测试结果:

        -- (1) OK
        select dbo.RATE(4*12, -200, 8000, 0, default, default) * 12  -- SQL formula
        0.0924 (9.24%)                                               -- SQL result
        =RATE(4*12, -200, 8000, 0) * 12                              -- Excel formula
        9.24%                                                        -- Excel result
        
        -- (2) OK
        select dbo.RATE(12, -1000, 12000, 0, default, default) * 12  -- SQL formula
        0 (0%)                                                       -- SQL result
        =RATE(12, -1000, 12000, 0) * 12                              -- Excel formula
        0%                                                           -- Excel result
        
        -- (3) OK
        select dbo.RATE(30, -400, 4000, 0, 1, default)    -- SQL formula
        0.10496 (10.496%)                                 -- SQL result
        =RATE(30, -400, 4000, 0, 1)                       -- Excel formula
        10.4964%                                          -- Excel result
        
        -- (4) OK
        select dbo.RATE(120, 28.1, -2400, 0, default, default)  -- SQL formula
        0.0059905810 (0.599%)                                   -- SQL result
        =RATE(120, 28.1, -2400, 0)                              -- Excel formula
        0.5991%                                                 -- Excel result
        
        -- (5) OK
        select dbo.RATE(10, -1000, 10000, -10000, default, default)  -- SQL formula
        0.1 (10%)                                                    -- SQL result
        =RATE(10, -1000, 10000, -10000)                              -- Excel formula
        10%                                                          -- Excel result
        
        -- (6) WRONG ANSWER (unless you set @guess to 0.01)
        select dbo.RATE(475, -1022.93, 272779.21, 0, default, default)  -- SQL formula
        0                                                               -- SQL result
        =RATE(475, -1022.93, 272779.21, 0, 0)                           -- Excel formula
        0.2716%                                                         -- Excel result
        
        -- (7) ERROR
        select dbo.RATE(252, -29002.85, 2500000, 0, default, default)  -- SQL formula
        invalid floating point operation                               -- SQL result
        =RATE(252, -29002.85, 2500000, 0)                              -- Excel formula
        1.0833%                                                        -- Excel result
        
        -- (8) OK
        select dbo.RATE(24, -46.14, 1000, 0, default, default)    -- SQL formula
        0.0083244385 (0.83244%)                                   -- SQL result
        =RATE(24, -46.14, 1000, 0)                                -- Excel formula
        0.8324%                                                   -- Excel result
        

        测试 (7) 和 (8) 取自 RATE Function from EXCEL in Swift providing different results,并使用 Newton-Raphson 方法寻找答案。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-10-17
          • 2017-05-05
          • 1970-01-01
          • 2016-03-16
          • 2015-01-11
          • 2020-09-17
          • 2019-06-27
          相关资源
          最近更新 更多