问题
I am trying to find the best rate of savings to achieve a down payment on a $1million house in 36 months. savings need to be within 100$ of required down payment. downpayment is 25% of total cost. I have to search for an integer between 0 and 10000 (using integer division), and then convert it to a decimal percentage (using float division) to use when we are calculating the current_savings after 36 months.
This is my code which is not working (I am really new to programming)
annual_salary = 150000
total_cost = 1000000
low=0
high=10000
portion_saved=(low+high)/20000.0
epsilon=100
current_savings = 0
portion_down_payment=0.25*total_cost
steps = 0
rate = 0.04
number_of_months = 0
semi_annual_raise = 0.07
while current_savings-portion_down_payment>=epsilon and number_of_months<=36:
current_savings += annual_salary * portion_saved / 12
current_savings += current_savings * rate / 12
number_of_months += 1
if number_of_months % 6 == 0:
annual_salary += annual_salary * semi_annual_raise
if current_savings<portion_down_payment:
low=portion_saved
else:
high=portion_saved
portion_saved=(low+high)/20000.0
steps+=1
print("Best savings rate: ", portion_saved)
print("Steps in bisection search", steps)
Any help is greatly appreciated !!!
回答1:
The monthly income does not vary with savings rate, so it makes sense to only calculate it once:
# calculate income per month over 36 months
base_monthly_salary = 150000 // 12
semiannual_raise = 0.07
monthly_incomes = [base_monthly_salary * (1. + semiannual_raise) ** (month // 6) for month in range(36)]
If the monthly savings do not earn interest, the problem is trivial:
target_amount = 1000000. * 0.25
savings_rate = target_amount / sum(monthly_incomes) # 0.4659859
so you would have to save 46.6% of income.
If monthly savings earn interest, the problem is more interesting (bad pun absolutely intended).
def savings_balance(monthly_incomes, monthly_interest_rate, savings_rate):
total = 0.
for amt in monthly_incomes:
# At the end of each month,
total *= 1. + monthly_interest_rate # we earn interest on what was already saved
total += amt * savings_rate # and add a fixed fraction of our monthly income
return total
Let's test it based on our calculation above,
savings_balance(monthly_incomes, 0.0, 0.4659859) # 249999.9467
so that looks like what we expected.
You can think of this function as iteratively evaluating a 36th-degree polynomial. Given known monthly_incomes
and interest_rate
, we want to find savings_rate
to produce a desired total
, ie find the only real positive root of polynomial - target == 0
. There is no analytic solution if interest_rate > 0.
, so we will try for a numeric solution.
target_amount = 1000000. * 0.25
# Make up a number: annual savings interest = 1.9%
monthly_interest_rate = 0.019 / 12.
# Our solver expects a single-argument function to solve, so let's make it one:
def fn(x):
return savings_balance(monthly_incomes, monthly_interest_rate, x)
def bisection_search(fn, lo, hi, target, tolerance=0.1):
# This function assumes that fn is monotonically increasing!
# check the end-points - is a solution possible?
fn_lo = fn(lo)
assert not target < -tolerance + fn_lo, "target is unattainably low"
if abs(target - fn_lo) <= tolerance:
return lo
fn_hi = fn(hi)
assert not fn_hi + tolerance < target, "target is unattainably high"
if abs(target - fn_hi) <= tolerance:
return hi
# a solution is possible but not yet found -
# repeat until we find it
while True:
# test the middle of the target range
mid = (lo + hi) / 2
fn_mid = fn(mid)
# is this an acceptable solution?
if abs(target - fn_mid) <= tolerance:
return mid
else:
# do we need to look in the lower or upper half?
if target < fn_mid:
# look lower - bring the top down
hi = mid
else:
# look higher - bring the bottom up
lo = mid
and now we run it like
# From above, we know that
# when interest = 0.0 we need a savings rate of 46.6%
#
# If interest > 0. the savings_rate should be smaller,
# because some of target_amount will be covered by generated interest.
#
# For a small annual_interest_rate over an N year term,
# the effective accrued interest rate will be close to
# N * annual_interest_rate / 2 -> 1.5 * 1.9% == 2.85%
#
# So we expect the required savings rate to be
# about 46.6% * (1. - 0.0285) == 45.3%
bisection_search(fn, 0.40, 0.47, target_amount) # 0.454047973
which gives a savings rate of 45.4%.
来源:https://stackoverflow.com/questions/45265517/python-bisection-search-mit-intro-to-programming-in-python-pset1-part-3