Bisection Search in Python - Find lowest payment over one year - python

I've been going nuts about this problem for hours, and I've been redoing it over and over! At this point I think I'm actually seeing numbers flying around me.
Anyway, I'm supposed to write a program that finds the correct amount of money to pay each month over one year, to pay off debt on a credit card. So with this program, there's a few conditions that must be met.
It must be done by using bisection search ((low + high) / 2)
There's a set balance
There's an annual interest rate.
Here's my code at the moment, and all I'm getting with this is infinite loops. What am I doing wrong here?
balance = 320000
annualInterestRate = 0.2
monthly_interest = float(annualInterestRate) / 12.0
lower_bound = float(balance / 12)
upper_bound = (balance * (1 + monthly_interest)**12) / 12.0
epsilon = 0.01
ans = float(lower_bound + upper_bound) / 2
while abs((balance * (1 + monthly_interest)**12) / 12.0) >= epsilon:
ans = float(lower_bound + upper_bound) / 2
total = float(ans * 12)
new_balance = 0
interest = 0
for month in range(0, 12):
interest += ans + (1 + monthly_interest)
new_balance += ans + interest
if new_balance > balance:
upper_bound = ans
print "low" + str(new_balance)
elif new_balance < balance:
lower_bound = ans
print "high" + str(new_balance)
else:
print "What's going on here?"
print "Lowest payment: %r" % ans

I believe there are a couple things wrong here, so first things first, your while is an infinite loop because the condition you are using will never converge to a solution (the variable values never change inside the loop). On top of that the condition (of the while) seems wrong for this kind of problem.
This is what I think you are trying to do, you are trying to find the upper and lower bounds for "the monthly payment" and the convergence condition for that is that the difference between those bounds should be less to a constant epsilon (in other words the error should be less than epsilon).
inside your loop you are calculating the midpoint correctly, this midpoint already is taking into account the interest but are calculating it again. The conditions to change the upper and lower bound are not taking into account the interest so this part of the code is a little messy.
So, modifying those conditions your program actually converges to a solution:
balance = 320000
annualInterestRate = 0.2
monthly_interest = float(annualInterestRate) / 12.0
lower_bound = float(balance / 12)
upper_bound = (balance * (2 + monthly_interest)**12) / 12.0
epsilon = 0.001
ans = float(lower_bound + upper_bound) / 2
total_debt=balance * (1 + annualInterestRate)
print total_debt
while (upper_bound - lower_bound) >= epsilon:
ans = float(lower_bound + upper_bound) / 2
total = float(ans * 12)
if total > total_debt:
upper_bound = ans
print "low " + str(total)
elif total < total_debt:
lower_bound = ans
print "high " + str(total)
else:
print "Solution found"
break
print "Lowest payment: %r" % ans
Hope it helps!

Related

MIT 6.0001 Pset 1c Bisectional Search question

Link to pset 1(https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/assignments/MIT6_0001F16_ps1.pdf)
Hello,
I've been working through the bisectional search problem for the MIT 6.0001 course's pset1. I feel like I have all components down, but it keeps giving me the same answer for savings rate and steps in bisectional search no matter what input I give. Could anyone tell me what I have done wrong here?
# User input
annual_salary = float(input('Enter the starting salary: ' ))
# Given sets of assumption
total_cost = 1000000
semi_annual_raise = 0.07
portion_down_payment = 0.25
current_savings = 0
r = 0.04 # Annual investment interest rate of current savings
down_payment = portion_down_payment * total_cost
num_guess = 0 # Bisectional search guess count starts at 0
# Lowest and highest integer savings rate in intial bisection search
low = 0
high = 10000
guess = (high + low)//2.0
while abs(down_payment - current_savings) >= 100:
current_savings = 0
rate = guess/10000
for month in range(36):
if month%6 == 0 and month > 0:
annual_salary += annual_salary * semi_annual_raise
monthly_salary = annual_salary/12
current_savings += (rate * monthly_salary) + current_savings*(r/12)
# Bisectional search introduced
if current_savings < down_payment:
low = guess
else:
high = guess
guess = (high + low)//2.0
num_guess += 1
if num_guess > 13:
break
print('Best savings rate:', rate)
print('Steps in bisection search:', num_guess)
You should reset the annual_salary variable to its original value (value from the input) each time before going through the [for loop] because each iteration tries a different saving rate guessing with the same starting salary.
I suggest you use a different variable (e.g. updated_annual_salary) and assign the starting salary to it to be able to reset it over and over.
Another thing is that when the annual salary gets updated, the monthly salary gets updated too and so does the saved portion. So, your [while loop] should start like this:
while abs(down_payment - current_savings) >= 100:
updated_annual_salary = annual_salary
current_savings = 0
rate = guess / 10000
for month in range(36):
if month % 6 == 0 and month > 0:
updated_annual_salary += updated_annual_salary * semi_annual_raise
monthly_salary = updated_annual_salary / 12
current_savings += (rate * monthly_salary) + current_savings * (r / 12)

I'm not sure how to account for the 36 month period in my bisection search

Forewarning, I have just started learning python so please bear with me on my beginner code.
So in the MIT6_0001 course on python. You are given a problem where you are supposed to find the "optimal monthly saving rate" for a 25% downpayment on a 1 million dollar house in a 36 month time period using a bisection search algorithm, where you're starting salary is 150,000 and a semi-annual raise of 7%. You must also account for an annual ROI 4% on your savings.
I have been working on this for a week. at first, I tried it without the for loop and that pretty much returned best savings rate as 100% which is where my main problem lies, I can't get around the fact that each time I bisect the value it will always be low so it just keeps trying to get closer and closer to 1 until the epsilon value is reached in the while loop. I have seriously been losing sleep over this please help
total_cost = 1000000
annual_salary =150000
current_savings = 0
epsilon = 100
low = 0
high = 10000
savings_rate= (low + high)/2
down_payment = .25 * total_cost
raise_rate = .07
month_count = 0
r = .04/12
step_count = 0
while current_savings-down_payment <= epsilon :
for i in range(36):
if month_count != 0 and month_count % 6 == 0:
annual_salary += annual_salary * raise_rate
current_savings += current_savings * r + (annual_salary/12) * (savings_rate/10000)
if current_savings < down_payment:
low = savings_rate
print("low =",low)
else:
high = savings_rate
print("high =",high)
month_count += 1
savings_rate= (low + high)/2
step_count += 1
print("Best savings rate:​ ",savings_rate/10000)
print("Steps in bisection sear: ",step_count)
expected output
Best savings rate:​ 0.4411
Steps in bisection search:​ 12
actual
Best savings rate:​ 0.5
Steps in bisection search:​ 1
Remember our goal is to take a continuous function and find the approximate points where it is zero in our graph. Our function in this case is abs(current_savings-down_payment)-epsilon = 0.I think the problem your having is that the absolute value of current savings minus down payment has to be less than epsilon and you are testing whether it is greater than epsilon instead.
I figured out the solution.
total_cost = 1000000
annual_salary = 150000
current_savings = 0
epsilon = 100
low = 0
high = 10000
down_payment = .25 * total_cost
raise_rate = .07
r = .04/12
step_count = 0
while abs(current_savings - down_payment) >= epsilon:
savings_rate = float(low + high) / 2.0 #take care of integer division
current_savings = 0 # reset for next itteration
annual_salary = 150000 #reset for next itteration
# print("savings_rate = %f " % (savings_rate/10000)) #4debugging
# print("current_savings= ", current_savings) #4debugging
for i in range(1,36): #initiate range at 1, easier to handle 6 month raise
if i != 1 and i % 6 == 0:
annual_salary += annual_salary * raise_rate
current_savings += current_savings * r + (annual_salary/12) * (savings_rate/10000)
# print("count =",i) #4debugging
# print("annual_salary: ", annual_salary) #4debugging
print ("current_savings=%f, down_payment=%f" % (current_savings, down_payment) )
print ("diff = %f " % abs(current_savings - down_payment) )
if (step_count > 20):
print('**********ERROR**********: inf loop')
break # exit infinite loop
elif current_savings < down_payment:
low = savings_rate #update range low if less than down payment
print("new range: %f -> %f" % (low, high) )
elif current_savings > down_payment:
high = savings_rate #update range high if greater than down payment
print("new range: %f -> %f" % (low, high) )
print("")
step_count += 1
print ("current_savings=%f, down_payment=%f" % (current_savings, down_payment) )
print ("diff = %f " % abs(current_savings - down_payment) )
print("best savings rate: ",savings_rate/10000)
print("number of bisection steps: ",step_count)

How to fix infinite loop during bisection search

My code passes test cases but if anything about ~949,000 is input it enters an infinite loop.
I need to calculate the best rate at which to save a portion of a monthly income to save in order to afford a down payment in 36 months with 2 significant digits. I'm thinking this has something to do with me not quite understanding how epsilon is calculated - I've tried calculating epsilon as 0.0001 * total_cost, 0.0004 * portion_down_payment, and 0.0001 * annual_income all to no avail.
#House Hunting ps1c
low = int(0)
high = int(10000)
percent_saved = (low + high)/2.0
current_savings = 0
annual_salary = int(input("What is your starting anual salary? "))
total_cost = 1000000
semi_annual_raise = 0.07
portion_down_payment = total_cost * 0.25
epsilon = 100
r = 0.04
total_months = 0
steps = 0
while True:
current_savings = 0
monthly_salary = annual_salary/12
for i in range(1,37):
current_savings += (current_savings*r/12)
current_savings += (monthly_salary * (percent_saved / 10000))
total_months += 1
if total_months % 6 == 0:
monthly_salary += monthly_salary * semi_annual_raise
steps +=1
if abs(current_savings - portion_down_payment) <= epsilon:
print("Steps in bisectional search: ", steps)
best_savings_rate = str(percent_saved / 100)
print("Best savings rate: ", (best_savings_rate + "%"))
break
elif (portion_down_payment - 100) - current_savings > 0:
low = percent_saved
percent_saved = int((low + high) / 2.0)
else:
high = percent_saved
percent_saved = int((low + high) / 2.0)
if percent_saved >= 9999:
print("It is not possible to afford this in 3 years")
break
Test Case 1
Enter the starting salary: 150000
Best savings rate: 0.4411
Steps in bisection search: 12
Test Case 2
Enter the starting salary: 300000
Best savings rate: 0.2206
Steps in bisection search: 9
Test Case 3
Enter the starting salary: 10000
It is not possible to pay the down payment in three years
My code passes all test cases but when the input is too high it enters an infinite loop that I don't know how to reconcile.
Essentially when annual salary becomes higher the optimal savings rates becomes smaller. When the optimal savings rate becomes smaller then the level of precision you require for
abs(current_savings - portion_down_payment) <= epsilon
becomes higher.
When you cast percent_saved to an int in
percent_saved = int((low + high) / 2.0)
it artificially limits the precision and then code enters an infinite loop.
Remove the cast and the code will work always.

Why is there a need to repeat formula in the loop?

I am currently self-studying MIT's Introduction to Computer Science and Programming in Python. However, I am having trouble with the loop part. Like in this case, why is there a need to repeat the formula ans = (high+low)/2 again in the while loop?
x = 25
epsilon = 0.01
numGuesses = 0
low = 0.0
high = max(1.0, x)
ans = (high + low)/2.0
while abs(ans**2 - x) >= epsilon:
print('low =', low, 'high =', high, 'ans =', ans)
numGuesses += 1
if ans**2 < x:
low = ans
else:
high = ans
ans = (high + low)/2.0
print('numGuesses =', numGuesses)
print(ans, 'is close to square root of', x)
It is a similar thing for problem set 1b, where I need to repeat the formula for monthly_saved.
annual_salary=float(input("Enter your starting annual salary:"))
monthly_salary=annual_salary/12
portion_saved=float(input("Enter the portion of salary to be saved:"))
total_cost=float(input("Enter the cost of your dream home:"))
semi_annual_raise=float(input("Enter your semi annual salary raise:"))
portion_down_payment=0.25 * total_cost
monthly_saved=portion_saved * monthly_salary
current_savings=0.0
months=0
annual_return = 0.04
while current_savings <= portion_down_payment:
monthly_return_investment=current_savings * (annual_return/12)
monthly_saved=portion_saved * monthly_salary #why must I include this line to obtain the correct answer?
current_savings = current_savings + monthly_saved + monthly_return_investment
months += 1
if months%6 == 0:
monthly_salary += monthly_salary*(semi_annual_raise)
print("Number of months",months)
Because you are updating the low and high variables inside the loop, and you want an updated ans value using the new values for the two variables.
You can see the loop's condition includes ans so it makes sense to update ans until the while condition is no longer true.
You can have do ... while loop like this:
while True:
do_staff()
if fail_condition:
break
or
do_staff()
while not fail_condition:
do_staff()

Bisection Search to find a min payment that causes an infinite loop

This is my first post, so apologies if it isn't the best!
I'm taking an online class (Edx) and attempting to write a bisection search that finds the minimum payment to clear a credit card balance. You are given the balance and annualInterestRate. My code runs into an infinite loop and I can't figure out what I'm doing wrong.
Supplied test conditions -- should produce Lowest Payment: 29157.09
balance = 320000
annualInterestRate = 0.2
monthlyInterestRate = annualInterestRate / 12.0
lower = balance / 12.0
upper = (balance * ((1 + monthlyInterestRate) ** 12.0)) / 12.0
guess = (upper + lower) / 2.0
valid = True
month = 0
def calculate(month, balance, guess, monthlyInterestRate):
while month < 12:
unpaidBalance = balance - guess
balance = unpaidBalance + (monthlyInterestRate * unpaidBalance)
month += 1
return balance
while valid == True:
if calculate(month, balance, guess, monthlyInterestRate) == 0:
print('Min payment is: ', str(round(guess,2)))
valid = False
elif calculate(month, balance, guess, monthlyInterestRate) > 0:
lower = guess
elif calculate(month, balance, guess, monthlyInterestRate) < 0:
upper = guess
else:
print('Invalid Input')
guess = (upper + lower) / 2.0
Any ideas on what I'm doing wrong? Thanks in advance for the help!

Categories

Resources