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

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!

Related

Why does changing how a bisection search is implemented make a runtime difference between two solutions to the same problem?

while going through a python programming problem from an MIT course, I got stuck trying to figure out the difference between two solutions, or more specifically, the reason behind why changing a part of the code made it run faster, while the original solution took so long that it kept on running (no infinite loop) and never delivered an output.
My solution that ran forever and did not give an output:
def bisection(i: int, j: int):
"""
Inputs: Integers, 'i' and 'j', representing the lower limit and the upper limit of the search.
Returns: Bisects the number of possibilites and returns the middle value.
"""
high = j
low = i
return (high+low)/2
semiannualraise = .07
rmonthly = 0.04/12
cost = 1_000_000
downpayment = cost*0.25
epsilon = 100
savings = 0
nummonths = 0
startingsalary = float(input("Enter Salary: "))
high = 10000
low = 0
portion_salary= 0
step = 0
salary = startingsalary
monthly_salary=salary/12
while True:
nummonths = 0
salary = startingsalary
while nummonths<36:
portion_of_salary = (bisection(low,high))/10000
step += 1
savings = ((salary/12)*portion_of_salary)+(savings*rmonthly)
nummonths +=1
if nummonths % 6 == 0:
salary = salary + (semiannualraise*salary)
if downpayment-savings < 100 and downpayment-savings >= 0:
break
elif downpayment-savings>= 100:
low = portion_of_salary*10000
else:
high = portion_of_salary*10000
print(f"Best savings rate: {portion_of_salary*100}%")
Solution by Linus
annual_salary = float(input('Enter the starting salary: '))
constant = annual_salary
semi_annual_rate = 0.07
r = 0.04
down_payment = 0.25
total_cost = 1000000
current_savings = 0
months = 0
bisection_count = 0
min = 0
max = 1
portion_saved = (max/2.0)/1000
if(annual_salary*3<down_payment*total_cost):
print('It is not possible to pay the down payment in three years.')
while(True):
while(months<36):
current_savings += (current_savings*r/12)+(portion_saved*(annual_salary/12))
months+=1
if(months % 6 == 0):
annual_salary += annual_salary*semi_annual_rate
if(current_savings >= down_payment*total_cost+100):
max = portion_saved
portion_saved = max/2.0
bisection_count+=1
months = 0
current_savings = 0
annual_salary = constant
elif(current_savings >= down_payment*total_cost-100 and current_savings <= down_payment*total_cost+100):
break
else:
min = portion_saved
portion_saved = (max+min)/2.0
bisection_count+=1
months = 0
current_savings = 0
annual_salary = constant
print('Best savings rate: ', portion_saved)
print('Steps in bisection search: ', bisection_count)
In my opinion, the difference is made only by the way we define our limits for the bisection search, and I am unable to quite get my head around what's happening there.
Note: Though I have been using stackoverflow for about a year to look for solutions, this would be my first ever post on here and its possible that I failed to ask a great question in accordance with How to ask a good question- StackOverflow. If so, please bear with me and also let me know what mistakes I made. Thank you.
I tried the following implementation for the bisection search:
def bisection(i: int, j: int):
"""
Inputs: Integers, 'i' and 'j', representing the lower limit and the upper limit of the search.
Returns: Bisects the number of possibilities and returns the middle value.
"""
high = j
low = i
return (high+low)/2
and later:
high = 10000
low = 0
and then:
elif downpayment-savings>= 100:
low = portion_of_salary*10000
else:
high = portion_of_salary*10000
and expected the same outputs as the other solution, but the code kept on running forever.
After some debugging, I found out the reason the first code does not give an output is not that it runs slow, but in fact enters an infinite loop in this part:
while nummonths<36:
portion_of_salary = (bisection(low,high))/10000
step += 1
savings = ((salary/12)*portion_of_salary)+(savings*rmonthly)
nummonths +=1
if nummonths % 6 == 0:
salary = salary + (semiannualraise*salary)
if downpayment-savings < 100 and downpayment-savings >= 0:
break
elif downpayment-savings>= 100:
low = portion_of_salary*10000
else:
high = portion_of_salary*10000
The program never exits this while loop because it never gets to the break condition, i.e, variable savings never exceeds variable downpayment.
The part causing this issue is at:
savings = ((salary/12)*portion_of_salary)+(savings*rmonthly)
Here, instead of incrementing the savings every month, the program assigns it to a new value every iteration and the savings never accumulates.
The fix is to simply add the missing '+' before '=':
savings += ((salary/12)*portion_of_salary)+(savings*rmonthly)

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)

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 in Python - Find lowest payment over one year

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!

Categories

Resources