【问题标题】:How to find lowest fixed balance in decimals using bisection in Ruby如何在 Ruby 中使用二分法找到小数中的最低固定余额
【发布时间】:2017-02-17 11:24:24
【问题描述】:

如何使用 ruby​​ 中的二分法找到 2 dp 的最低固定月供以偿还 12 个月的复利余额?

免责声明:我正确地应用了 upperBound 和 unpaidBalance 公式。

测试用例:

find_fixed_payment(320000, 0.2) should return 29157.09 

但是我当前的方法返回的中间值是 29591.87,这对于固定支付来说已经绰绰有余了(需要减少到最低)。

我的代码哪里出错了,因为我不确定如何进行二分(我使用二进制搜索)?

def find_fixed_payment(balance,annualInterestRate)
    lowerBound = balance/12
    upperBound = (balance * (1 + annualInterestRate/12)**12) / 12.0
    unpaidBalance = balance

    while true
         middle = (lowerBound + upperBound )/2
         #use middle as the monthly payment at first
        (1..12).each do |i|   
            unpaidBalance = (unpaidBalance - middle) +
              annualInterestRate / 12 * (unpaidBalance - middle)      
        end 

        temp = unpaidBalance.floor
        if temp < 0
            middle -= 0.01
            upperBound = middle
            # should go to for loop to reduce unpaid balance 
            # with new middle but not happening, why?
        else
            middle += 0.01
            lowerBound = middle
        end

        if temp == 0 
            return middle
        end

        if upperBound == lower+1
            return -1
        end

        return middle.round(2)
    end
end

【问题讨论】:

  • @mudasobwa 对我来说,代码看起来并不像一团糟,不管用“整个代码都是一团糟”开头的其他有用的评论都是适得其反和卑鄙的。
  • @Jordan while true 而不是loop ⇒ 滥用each,更新内部的外部局部变量⇒ 未使用|i| ⇒ DRY 违反(unpaidBalance - middle) ⇒ 冗余temp 变量⇒三个if 分支而不是case 的范围⇒ 奇怪的+0.01temp.zero? ⇒ 三个后续的returns :: 所有这一切让我阅读这段代码的时间比不凌乱的代码要长十倍。以上是OP没有发现问题的原因,我只是指出了这一点。
  • @mudasobwa 不管怎样,用“整个代码都是一团糟”来开头有用的评论会适得其反,而且是卑鄙的。一个小时后解释为什么你认为这是一团糟并不能原谅最初的行为。

标签: ruby bisection


【解决方案1】:

您犯了很多错误,我假设您是从某处的阅读中抄录此方法,并且根本无法理解它所描述的过程。

基本上你初始化所有的起始变量,然后你需要创建循环。一旦进入循环,每次计算时,您都需要将余额重置回 320,000,否则它会变成一个移动的目标,我们永远不会得到您想要的答案。

我在整数 12 上使用了 times 方法来模拟 12 个月的周期,在您的尝试中,您使用了一个范围并调用了 each 方法。

现在,我不确定您为什么要尝试像那样调整 middle 的值,但这是一个有争议的问题,因为一旦它循环回到您的 while true 循环的开头,它就会回到原始中间值。在我的版本中,我适当地更改了上限或下限。把它想象成一个 1 到 10 的数字猜测,你足够聪明,知道你每次只选择中间直到你得到它,你会从 1 的下限和 10 的上限开始,然后猜 5。如果我告诉你更高,你现在知道 5 是你的下限,而 10 仍然是你的上限。

注意:我希望这会有所帮助,并欢迎其他用户随时批评和重构我的代码,我最近才开始使用 ruby​​。

def find_fixed_payment(balance,annualInterestRate)
    monthlyInterestRate = annualInterestRate / 12 
    lowerBound = balance/12
    upperBound = (balance * (1 + monthlyInterestRate)**12) / 12.0
    unpaidBalance = balance
    accuracy = 0.01

    while unpaidBalance.abs > accuracy 
      balance = unpaidBalance
      middle = (lowerBound + upperBound) / 2
      12.times do
        newUnpaidBalance = unpaidBalance - middle
        monthInterest = monthlyInterestRate * newUnpaidBalance
        unpaidBalance = newUnpaidBalance + monthInterest
      end
      if unpaidBalance < 0
          upperBound = middle
          unpaidBalance = balance
      elsif unpaidBalance > accuracy
          lowerBound = middle
          unpaidBalance = balance
      end
    end
    middle.round(2)
end


find_fixed_payment(320000, 0.2)

【讨论】:

  • 谢谢!这工作完全。在速度/时间方面,我可以检查使用 12.times do 与每个 do 相比有什么区别吗?这个解决方案的复杂度也是 O(log n),因为它使用了二进制搜索算法?
  • Θ(log(n)) 只是一个运行 10,000 次的快速简单的基准测试:当前:300.252971 毫秒使用 (1..12).each 执行:303.018081 毫秒
猜你喜欢
  • 1970-01-01
  • 2011-05-24
  • 2018-04-03
  • 2022-01-05
  • 1970-01-01
  • 1970-01-01
  • 2010-11-24
  • 1970-01-01
  • 2011-09-23
相关资源
最近更新 更多