Search code examples
rubybisection

How to find lowest fixed balance in decimals using bisection in Ruby


How do I find the LOWEST FIXED monthly payment in 2 dp to pay off the balance with compounded interest for 12 months using bisection in ruby?

Disclaimer: I am applying upperBound and unpaidBalance formula correctly.

Test case:

find_fixed_payment(320000, 0.2) should return 29157.09 

But my current method returns the middle value of 29591.87 which is way more than enough as the fixed payment (need to reduce to the lowest).

Where did my code went wrong as I am not sure how to do bisection (im using binary search)?

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

Solution

  • You made a lot of errors, I'm assuming you were transcribing this method from a reading somewhere, and simply failed to understand the process it was describing.

    Basically you initialize all your starting variables, then you need the create the loop. Once in the loop, each time through you're calculating, you'll need to reset the balance back to 320,000 or it becomes a bit of a moving target and we'll never get the answer you're looking for.

    I used the times method on the integer 12 to simulate the 12 month cycle, in your attempt you used a range and called the each method.

    Now, I'm not sure why you were trying to adjust the value of middle like that, but it was a moot point because once it looped back to the beginning of your while true loop, it just went back to the original middle value. In my version I changed the upper or lower bound appropriately. Think of it like a number guessing of 1-10, you're smart enough to know you just pick the middle every time until you get it, and you would start with a lower bound of 1 and upper of 10, then guess 5. If I told you higher, you now know that 5 is your lower and 10 is still your upper.

    Note: I hope this helps, and welcome other users feel free to critique and refactor my code, I only started ruby recently.

    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)