My homework problem: Find the smallest monthly payment required pay off a given loan principal within a year. One-twelfth of the original balance is a good lower bound; a good upper bound is one-twelfth of the balance, after having its interest compounded monthly for an entire year.
In short:
Monthly interest rate = (Annual interest rate) / 12.0
Monthly payment lower bound = Balance / 12
Monthly payment upper bound = (Balance * (1 + Monthly interest rate)**12) / 12.0
I have to write a bisection search to find that smallest monthly payment to the cent.
Every time I run this code I get the lowest payment to be a value a couple hundred off from the correct solution.
balance = 414866
annualInterestRate = 0.22
month = 0
monthlyinterest = (annualInterestRate) / 12.0
updatedbalance = balance
monlowbound = balance / 12
monupbound = (balance * (1 + monthlyinterest)**12) / 12.0
mid = (monlowbound + monupbound) /2
minpay = 0
while balance > 0 and month <= 12:
balance = updatedbalance
updatedbalance = ((monthlyinterest * balance) + balance) - minpay
month += 1
if updatedbalance > 0:
minpay = (mid + monupbound)/2
mid = monlowbound
if updatedbalance < 0:
minpay = (monlowbound + mid)/2
monupbound = mid
else:
print("Lowest payment:" + " " + str(round(minpay,2)))
This is what I get as the output:
Lowest payment: 40888.41
Lowest payment: 38783.0
Lowest payment: 38783.0
Lowest payment: 38783.0
Lowest payment: 38783.0
Lowest payment: 38783.0
Lowest payment: 38783.0
Lowest payment: 38783.0
Lowest payment: 38783.0
Lowest payment: 38783.0
Lowest payment: 38783.0
Lowest payment: 38783.0
Lowest payment: 38783.0
The major problem is that you apply your feedback adjustment logic (adjusting the monthly payment) every month. You need to wait until the end of the year, and then adjust the payment. All of that should be wrapped inside a while loop that continues until you get "close enough" ... say, within a full penny of the previous payment. Something like this:
last_pay = -1000 # Ridiculous value to start the process
while abs(last_pay - minpay) > 0.01:
# use your current logic for paying off one year, including
...
for month in range(12):
....
# HERE is where you check the final balance
# to see whether you're high or low.
# Then adjust your monthly payment (minpay)
Does this get you going?
Your algorithm does not work.
What you want is :
A function f that gives you the final balance for any fixed payment
An algorithm to find the root of this function, i.e. the monthly fixed payment that needs to be paid for the final balance to be as close as zero as possible. With a given f function, a recursive approach would be something like :
You seem to be doing both at the same time, i.e. you change your "mid" value while you're still computing. I suggest you write down your algorithm before you try to code it to realize the flow you probably want :
def finalbalance(fixedpayment):
#code that determines the balance status
def solvebisection(lowbound,highbound,function=finalbalance,tofind=0):
#Recursive coding to "find the root"
Related
My program is supposed to tell users how many months it will take to double the money in their investment account. I am able to do the calculations correctly, but I'm unable to break out of the loop and print the statement that tells the user the final sentence "It will take x months to double your investment with a y% return".
balance = int(input("Enter an initial Roth IRA deposit amount:"))
apr = int(input("Enter an annual percent rate of return:"))
month = 0
while balance != 2*balance:
added_interest = balance * (apr / 100) / 12
balance = balance + added_interest
month +=1
formatted_balance = "${:.2f}".format(balance)
print("Value after month", month,":", formatted_balance)
if balance == 2*balance:
break
print("It will take", month, "months to double your investment with a", apr, "% return")
Your problem is that testing balance against 2*balance is always testing the current balance, not double the initial balance. Just store off the computed doubled balance initially, and test if the current balance is still less than that (no need for separate if/break, your while condition will handle it):
balance = int(input("Enter an initial Roth IRA deposit amount:"))
apr = int(input("Enter an annual percent rate of return:"))
month = 0
doubled_balance = 2 * balance # Store off doubled initial balance
while balance < doubled_balance: # Check current balance against doubled initial,
# and use <, not !=, so you stop when you exceed it,
# not just when you're exactly equal
added_interest = balance * (apr / 100) / 12
balance = balance + added_interest
month +=1
formatted_balance = "${:.2f}".format(balance)
print("Value after month", month,":", formatted_balance)
# No need for if/break
print("It will take", month, "months to double your investment with a", apr, "% return")
All that said, this doesn't need a loop at all. The initial balance doesn't matter (it takes just as long to double $1 as to double $1000 with a fixed rate of return, ignoring rounding errors), so this reduces to a simple conversion for APR to APY to account for monthly compounding, followed by a logarithm computation to figure out what power of the APY is necessary to reach 2 (a doubling), then convert from months to years and round up (since you won't double until the end of that month):
import math
apr = int(input("Enter an annual percent rate of return:"))
apr_decimal = apr / 100
apy = (1 + (apr_decimal / 12)) ** 12 # Standard APR to APY computation for monthly compounding
months = math.ceil(math.log(2, apy) * 12) # Compute the power (years) of APY to double the investment
# then multiply by 12 and round up to a full month
print("It will take", months, "months to double your investment with a", apr, "% return")
In your comparison you compared balance with 2*balance. Obviously balance == 2*balance will always be false, if balance > 0. So your code is stuck there forever if someone plans to invest anything more than $0.
You need a new variable to store the updated balance with rate of return:
# assuming you have balance and apr set
newBalance = balance # balance + returned amount
month = 0
while newBalance < 2*balance:
# replace ! with <, so that loop breaks when newBalance >= 2*balance
added_interest = newBalance * (apr / 100) / 12
newBalance = newBalance + added_interest
month +=1
formatted_balance = "${:.2f}".format(newBalance)
print("Value after month", month,":", formatted_balance)
The code above assumes that the added interest is calculated based on newBalance value. Please tell me if this does not work for you. I will debug it later.
Perhaps a little bit simpler solution.
from math import log, ceil
apr = int(input("Enter an annual percent rate of return: "))
rate = apr/12/100 # monthly rate
months = ceil(log(2)/rate)
print("It will take", months, "months to double your investment with a", apr, "% return")
prints:
Enter an annual percent rate of return: 10
It will take 84 months to double your investment with a 10 % return
This was calculated using the formula for continuous compounding. With a FV (future value) = PV (present value) * e**(rate * periods).
FV = PV * e**(rate*periods)
FV/PV = e**(rate*periods)
log(FV/PV) = log(e**(rate*periods))
log(FV/PV) = (rate*periods) * log(e)
log(FV/PV) = (rate*periods) * 1
log(FV/PV)/rate = periods
Since you want to find the months to double your initial investment, FV/PV will equal 2, so:
log(2)/rate = periods. (The log(e) == 1 above)
e and compound interest is defined here.
I am currently participating in a free online course through MIT OCW. I was given a certain problem and I am trying to understand the solution. I am not sure what the "2" is doing. it is the two in the (minMonthlyPaymentRate * balance, 2) Here is the solution:
# 6.00 PS1-A Solution
# Determines remaining credit card balance after a year of making the minimum payment each month
balance = float(raw_input("Enter the outstanding balance on your credit card: "))
annualInterestRate = float(raw_input("Enter the annual credit card interest rate as a decimal: "))
minMonthlyPaymentRate = float(raw_input("Enter the minimum monthly payment rate as a decimal: "))
# Monthly Interest Rate
monthlyInterestRate = annualInterestRate/12
# Initialize state variables
numMonths = 1
totalAmtPaid = 0
while numMonths <= 12:
# Minimum monthly payment of balance at start of the month
minPayment = round(minMonthlyPaymentRate * balance,2)**This Two!?**
totalAmtPaid += minPayment
# Amt of monthly payment that goes to interest
interestPaid = round(monthlyInterestRate * balance,2)
# Amt of principal paid off
principalPaid = minPayment - interestPaid
# Subtract monthly payment from outstanding balance
balance -= principalPaid
print "Month:", numMonths
print "Minimum monthly payment:", minPayment
print "Remaining balance:", balance
# Count this as a new month
numMonths += 1
print "RESULT"
print "Total amount paid:",totalAmtPaid
print "Remaining balance:",balance
round() which rounds off to the given number of digits and returns the floating point number.
Example code:
val=2.665
print(round(val, 2);
output:
6.67
As seen in the python documentation of the function round() it states "rounded to ndigits digits after the decimal point". So it means the result will be rounded to two digits after the decimal point.
One picture is worth a thousand words.
Suppose you have an investment plan where you invest a certain fixed amount at the beginning of every year. Compute the total value of the investment at the end of the last year. The inputs will be the amount to invest each year, the interest rate, and the number of years of the investment.
This program calculates the future value
of a constant yearly investment.
Enter the yearly investment: 200
Enter the annual interest rate: .06
Enter the number of years: 12
The value in 12 years is: 3576.427533818945
I've tried a few different things, like below, but it doesn't give me that 3576.42, it gives me only $400. Any ideas?
principal = eval(input("Enter the yearly investment: "))
apr = eval(input("Enter the annual interest rate: "))
years = eval(input("Enter the number of years: "))
for i in range(years):
principal = principal * (1+apr)
print("The value in 12 years is: ", principal)
If it's a yearly investment, you should add it every year:
yearly = float(input("Enter the yearly investment: "))
apr = float(input("Enter the annual interest rate: "))
years = int(input("Enter the number of years: "))
total = 0
for i in range(years):
total += yearly
total *= 1 + apr
print("The value in 12 years is: ", total)
With your inputs, this outputs
('The value in 12 years is: ', 3576.427533818945)
Update: Responding to your questions from the comments, to clarify what's going on:
1) You can use int() for yearly and get the same answer, which is fine if you always invest a whole number of currency. Using a float works just as well but also allows the amount to be 199.99, for example.
2) += and *= are convenient shorthand: total += yearly means total = total + yearly. It's a little easier to type, but more important, it more clearly expresses the meaning. I read it like this
for i in range(years): # For each year
total += yearly # Grow the total by adding the yearly investment to it
total *= 1 + apr # Grow the total by multiplying it by (1 + apr)
The longer form just isn't as clear:
for i in range(years): # For each year
total = total + yearly # Add total and yearly and assign that to total
total = total * (1 + apr) # Multiply total by (1 + apr) and assign that to total
It can be done analytically:
"""
pmt = investment per period
r = interest rate per period
n = number of periods
v0 = initial value
"""
fv = lambda pmt, r, n, v0=0: pmt * ((1.0+r)**n-1)/r + v0*(1+r)**n
fv(200, 0.09, 10, 2000)
Similarly, if you are trying to figure out the amount you need to invest so you get to a certain number, you can do:
pmt = lambda fv, r, n, v0=0: (fv - v0*(1+r)**n) * r/((1.0+r)**n-1)
pmt(1000000, 0.09, 20, 0)
As suggested in the comments, you shouldn't use eval() here. (More info on eval can be found in the Python Docs). -- Instead, change your code to use float() or int() where applicable, as shown below.
Also, your print() statement printed out the parenthesis and comma, which I expect you didn't want. I cleaned it up in the code below, but if what you wanted is what you had feel free to put it back.
principal = float(input("Enter the yearly investment: "))
apr = float(input("Enter the annual interest rate: "))
# Note that years has to be int() because of range()
years = int(input("Enter the number of years: "))
for i in range(years):
principal = principal * (1+apr)
print "The value in 12 years is: %f" % principal
I'm writing a credit card payment calculator for my Python class. The assignment is to write a definition for a calculator that figures out the monthly payment necessary to bring your balance to zero in x number of months.
The definition takes 3 parameters: initialBalance, apr, months.
As much as I can figure, the point of the assignment is to get us to use bisection method to find our answer, and I have written two other definitions that aid the assignment:
1) newBalance() - which determines the new balance after a payment is made;
2) balances() - which returns a list of balances after the payments were made;
In that light, balances()[-1] returns the final balance, so my plan of action has been to evaluate the last item in the list as equal to 0 (or at least within 0.005 of 0!) and if so, return the payment that got me there.
if the final balance is negative (I've paid too much!): payment = payment - (payment / 2);
if the balance is positive (I haven't paid enough!): payment = payment + (payment / 2);
As best as I can figure it, my algorithm should eventually conclude, but it NEVER finds a close enough answer...
Here is my code, (and the Prof's testing def at the end):
def newBalance(prevBalance, apr, payment):
"""
- prevBalance: the balance on the credit card statement.
- apr: the annual percentage rate (15.9 here means 15.9%).
- payment: the amount paid this month to the credit card company.
- returns: the new balance that will be owed on the credit card
(assumes no purchases are made).
"""
interestCharge = float(((apr / 12.0) / 100) * prevBalance)
return float((interestCharge + prevBalance) - payment)
def balances(initialBalance, apr, payments):
"""
- initialBalance: the initial balance on the credit card.
- apr: the annual percentage rate (15.9 here means 15.9%).
- payments: a list of monthly payments made on the credit card.
- returns: a list giving the balance on the credit card each
month. The first number in the list is the initial
balance, the next number is the balance after the first
payment is made, and so on. Note that the length of the returned
list is len(payments) + 1.
"""
balancelist = []
balancelist.append(initialBalance)
for x in range(0, len(payments)):
balancelist.append(newBalance(balancelist[x], apr, payments[x]))
return balancelist
def findMonthlyPayment(initialBalance, apr, months):
"""
- initialBalance: the starting balance on the card.
- apr: the APR.
- months: the number of equal monthly payments you wish to
make in order to reduce the balance to zero.
- returns: the monthly payment amount needed to reduce the
balance to zero (well, "zero" to within $0.005, anyway)
in the given number of months.
"""
guess = float(initialBalance / months)
listo = months*[guess]
while True:
if abs(float(balances(initialBalance, apr, listo)[-1]) - initialBalance) < 0.006:
print "BINGO", guess ##for debugging
print balances(initialBalance, apr, listo)[-1]
return guess
else:
if float(balances(initialBalance, apr, listo)[-1]) < -0.005:
guess = guess - (guess / 2)
print "BOO", guess ##for debugging
print balances(initialBalance, apr, listo)[-1]
else:
guess = guess + (guess / 2)
print "NAY", guess ##for debugging
print balances(initialBalance, apr, listo)[-1]
listo = months*[guess]
def testFindMonthlyPayment():
answer = findMonthlyPayment(1000, 18.9, 60)
print
myString = "Monthly payment to pay off $%.2f in %d months at %.2f%% APR:"
print myString % (1000, 60, 18.9)
print "$%.2f" % answer
# Output should look approximately like this:
"""
iteration: 1 guess: 500.0 final bal: -46777.3384635
iteration: 2 guess: 250.0 final balance: -22111.7016729
iteration: 3 guess: 125.0 final balance: -9778.88327752
iteration: 4 guess: 62.5 final balance: -3612.47407985
iteration: 5 guess: 31.25 final balance: -529.269481021
iteration: 6 guess: 15.625 final balance: 1012.3328184
iteration: 7 guess: 23.4375 final balance: 241.531668687
iteration: 8 guess: 27.34375 final balance: -143.868906167
iteration: 9 guess: 25.390625 final balance: 48.83138126
iteration: 10 guess: 26.3671875 final balance: -47.5187624535
iteration: 11 guess: 25.87890625 final balance: 0.656309403241
iteration: 12 guess: 26.123046875 final balance: -23.4312265251
iteration: 13 guess: 26.0009765625 final balance: -11.387458561
iteration: 14 guess: 25.9399414062 final balance: -5.36557457885
iteration: 15 guess: 25.9094238281 final balance: -2.35463258781
iteration: 16 guess: 25.8941650391 final balance: -0.849161592282
iteration: 17 guess: 25.8865356445 final balance: -0.0964260945206
iteration: 18 guess: 25.8827209473 final balance: 0.27994165436
iteration: 19 guess: 25.8846282959 final balance: 0.0917577799204
iteration: 20 guess: 25.8855819702 final balance: -0.00233415730086
Monthly payment to pay off $1000.00 in 60 months at 18.90 APR:
$25.89
"""
Thanks for the help. Probably wouldn't have joined compsci unless everything I ever googled was on stackoverflow.
This isn't how you bisect
guess = guess - (guess / 2)
Normally you keep a low_guess and a high_guess. You try
guess = (low_guess+high_guess)/2
and then based on the result, you either set
low_guess = guess
or
high_guess = guess
and repeat
Note: In Python2, / is integer divison if the denominator and numerator are both ints, so it's best to just make sure the initial guess is a float
def findMonthlyPayment(initialBalance, apr, months):
"""
- initialBalance: the starting balance on the card.
- apr: the APR.
- months: the number of equal monthly payments you wish to
make in order to reduce the balance to zero.
- returns: the monthly payment amount needed to reduce the
balance to zero (well, "zero" to within $0.005, anyway)
in the given number of months.
"""
low_guess = 0
high_guess = initialBalance
guess = float((low_guess + high_guess) / 2)
listo = months*[guess]
while True:
if abs(float(balances(initialBalance, apr, listo)[-1])) < 0.006:
print "BINGO", guess ##for debugging
print balances(initialBalance, apr, listo)[-1]
return guess
elif float(balances(initialBalance, apr, listo)[-1]) < -0.005:
high_guess = guess
print "BOO", guess ##for debugging
print balances(initialBalance, apr, listo)[-1]
else:
low_guess = guess
print "NAY", guess ##for debugging
print balances(initialBalance, apr, listo)[-1]
guess = float((low_guess + high_guess) / 2)
listo = months*[guess]
I am a python/programming beginner. I was assigned a problem on MIT open courseware to:
Write a program that calculates the minimum fixed monthly payment in order to pay off a credit card balance within 12 months.
Take as raw_input() the following floating point numbers:
1) the outstanding balance on the credit card
2) the annual interest rate as a decimal
Print out the fixed minimum payment, number of months(at most 12 and possibly less than 12) it takes to pay off the debt, and the balance (likely to be a negative number).
Assume the interest is compounded monthly according to the balance at the start of the month(before the payment for that month is made). The monthly payment must be a multiple of $10 and is the same for all months. Notice that it is possible for the balance to become negative using this payment scheme.
THE ANSWER IS:
balance = float(raw_input('Enter the outstanding balance on your credit card: '))
interest = float(raw_input('Enter the annual credit card interest rate as a decimal: '))
minPay = 10
newBalance = balance
while balance > 0:
for month in range(1,13):
newBalance = newBalance*(1+(interest/12))-minPay
if newBalance <=0:
break
if newBalance <= 0:
balance = newBalance
else:
newBalance = balance
minPay = minPay+10
print 'RESULT'
print 'Monthly payment to pay off debt in 1 year: ' + str(minPay)
print 'Number of months needed: ' + str(month)
print 'Balance: ' + str(round(balance,2))
MY QUESTIONS:
1) Using 1200 as the raw input balance, and .18 as the interest rate. Could someone explain in words how you would arrive at minPay = 120, month = 11, and balance = - 10.05?
I am confused by the newBalance = newBalance* (1 +(interest/12)) - minPay.
Using 1200 as the balance would make newBalance = 1200 * (1 +(.18/12)) - 10 = 1218 - 10 = 1208.
2) Since newBalance is not <= 0 the program would then proceed to the else statement. What is happening with the newBalance = balance part of the else statement. Does that assign newBalance back to 1200(original balance input).
I am having some trouble understanding this problem. Any insight whatsoever would be appreciated.
I would strongly recommend going through each line of the code as a python interpreter would and then seeing, why the program does perform as expected.
If you are lazy for that, try to put a print statement within the loop itself to see what the values of each variable are at every step. This always helps me figure out, what the code is doing.
As for your questions,
Yes, New balance does have the value that you expect it to have
Yes, it does get reassigned to balance, in the else part. You might want to change this.
As, I do not understand what the code is actually trying to do, I cannot help you with what the correct approach is, but try to add print statements, and it should help. Good Luck!
What is happening with the newBalance = balance part of the else statement? Does that assign newBalance back to 1200(original balance input).
Yes that's it. Then the minPay is increased and the loop starts again.
As minPay increases at each iteration, newBalance will end being negative.
balance = 3329
annualInterestRate = 0.2
minimum_fixed_payment = 10
unpaid_balance = balance
MonthlyInterestRate = annualInterestRate / 12
month_count = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
while balance > 0:
for month in month_count:
unpaid_balance = unpaid_balance - minimum_fixed_payment
interest = MonthlyInterestRate * unpaid_balance
unpaid_balance = unpaid_balance + interest
if unpaid_balance <= 0:
break
if unpaid_balance <= 0:
balance = unpaid_balance
else:
unpaid_balance = balance
minimum_fixed_payment = minimum_fixed_payment + 10
print "Lowest Payment: %s" % (minimum_fixed_payment)
The easiest way to answer this question would be to see it from this angle and that is, using the minimum fixed payment, we subtract the minimum fixed payment from the unpaid balance every month and check if the value left is lesser than 0. if it is not we increase the minimum fixed payment by 10 and repeat the process until we have a balance that is lesser than or equal to zero.
That is the modification I made to the code.