Skip to content

Instantly share code, notes, and snippets.

@lukeredpath
Last active December 14, 2015 06:29
Show Gist options
  • Save lukeredpath/5043351 to your computer and use it in GitHub Desktop.
Save lukeredpath/5043351 to your computer and use it in GitHub Desktop.
Find the lowest possible monthly payment to pay off balance in full with compound interest, recursive solution.
EPISILON = 0.01 # calculate to the nearest cent
def calculate_balance_remaining_after_year(starting_balance, monthly_payment, annual_interest_rate)
12.times.reduce(starting_balance) do |balance|
balance -= monthly_payment
balance += (annual_interest_rate / 12) * balance
end
end
def calculate_lowest_payment_within_bounds(starting_balance, annual_interest_rate, lower_bound, upper_bound)
median_payment = (lower_bound + upper_bound) / 2.0
balance = calculate_balance_remaining_after_year(starting_balance, median_payment, annual_interest_rate)
if balance > EPISILON
calculate_lowest_payment_within_bounds(starting_balance, annual_interest_rate, median_payment, upper_bound)
elsif balance < -EPISILON
calculate_lowest_payment_within_bounds(starting_balance, annual_interest_rate, lower_bound, median_payment)
else
median_payment
end
end
def find_lowest_monthly_payment(starting_balance, annual_interest_rate)
minimum_monthly_payment = starting_balance / 12.0
maximum_monthly_payment = calculate_balance_remaining_after_year(starting_balance, 0, annual_interest_rate) / 12.0
calculate_lowest_payment_within_bounds(
starting_balance, annual_interest_rate, minimum_monthly_payment, maximum_monthly_payment
)
end
if __FILE__ == $0 # this just means run this block if we run the file directly
require 'test/unit'
class BalanceCalculatorTest < Test::Unit::TestCase
def test_equal_monthly_payments_with_no_interest
balance = calculate_balance_remaining_after_year(1200.0, 100.0, 0)
assert_equal 0, balance
end
def test_no_monthly_payments_with_no_interest
balance = calculate_balance_remaining_after_year(1200.0, 0, 0)
assert_equal 1200.0, balance
end
def test_no_monthly_payments_with_interest
balance = calculate_balance_remaining_after_year(1200.0, 0, 0.12)
assert_in_delta 1352.19, balance, 0.01
end
def test_equal_monthly_payments_with_interest
balance = calculate_balance_remaining_after_year(1200.0, 100.0, 0.12)
assert_in_delta 71.26, balance, 0.01
end
def test_overpayment_with_no_interest
balance = calculate_balance_remaining_after_year(1200.0, 110.0, 0)
assert_equal -120.0, balance
end
def test_calculated_lowest_monthly_payment_actually_pays_off_balance_in_full_to_nearest_cent
starting_balance = 1200.0
interest_rate = 0.12
lowest_monthly_payment = find_lowest_monthly_payment(starting_balance, interest_rate)
balance = calculate_balance_remaining_after_year(starting_balance, lowest_monthly_payment, interest_rate)
assert_in_delta 0, balance, 0.01
end
def test_calculated_lowest_monthly_payment_for_large_values
starting_balance = 320000.0
interest_rate = 0.2
lowest_monthly_payment = find_lowest_monthly_payment(starting_balance, interest_rate)
balance = calculate_balance_remaining_after_year(starting_balance, lowest_monthly_payment, interest_rate)
assert_in_delta 0, balance, 0.01
end
def test_calculated_lowest_monthly_payment_for_katies_inputs_matches_python_result
starting_balance = 320000.0
interest_rate = 0.2
lowest_monthly_payment = find_lowest_monthly_payment(starting_balance, interest_rate)
balance = calculate_balance_remaining_after_year(starting_balance, lowest_monthly_payment, interest_rate)
assert_in_delta 0, balance, 0.01
assert_in_delta 29157.09, lowest_monthly_payment, 0.01
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment