简体   繁体   中英

How do I change a condition within a loop, but only after the first iteration?

I'm creating a simple model which calculates the minimum fixed monthly payment required to pay off a credit card balance within 12 months.

My code:

balance = 3329             # starting balance
annualInterestRate = 0.2   # yearly interest rate
minFixedPayment = 0        # initiate a minimum monthly payment of 0

while balance > 0:
    minFixedPayment = minFixedPayment + 10
    for i in range(1,13,1):
        unpaidBalance = balance - minFixedPayment
        balance = unpaidBalance + ((annualInterestRate/12) * unpaidBalance)
        i=i+1
print(round(minFixedPayment,2))

I added the "minFixedPayment" increment BEORE the for loop so that it keeps incrementing until it finds a value where balance is <= 0 at the end of month 12. I know this is wrong since it will compute the first iteration with minFixedPayment = 10, not 0. Is there a better way to arrange this?

I've tried adding the

minFixedPayment = minFixedPayment + 10

statement to the end of the loop, like this:

while balance > 0:
    for i in range(1,13,1):
        unpaidBalance = balance - minFixedPayment
        balance = unpaidBalance + ((annualInterestRate/12) * unpaidBalance)
        i=i+1
    minFixedPayment = minFixedPayment + 10 # moved this to after the loop
print(round(minFixedPayment,2))

but that makes the while loop run forever (why?).

Is there a better way to approach this? Thank you for your time.

Some issues:

  • As your intention is to try with different payment amounts, you should reset the balance to its original value with each attempt. Without that, you can get into an infinite loop with an ever increasing balance. In order to be able to reset the balance, you'll need to store it in a second name as well.
  • You should not increase i with i=i+1 . That is already taken care of with the for loop.

Here is the suggested code:

loan = balance = 3329      # starting balance, use two names so you can restart
annualInterestRate = 0.2   # yearly interest rate
minFixedPayment = -10      # initiate a minimum monthly payment

monthlyInterestRate = annualInterestRate/12 # avoid doing this repeatedly

while balance > 0:
    minFixedPayment = minFixedPayment + 10
    balance = loan # start from scratch
    for i in range(1,13):
        unpaidBalance = balance - minFixedPayment
        balance = unpaidBalance + monthlyInterestRate * unpaidBalance

print(round(minFixedPayment,2))

For your interest, here is a more sophisticated solver:

from functools import partial
from math import ceil

def diff(fn, x, h=0.001):
    """
    Numerically differentiate fn at x
    """
    return (fn(x + h) - fn(x)) / h

def newton_solver(fn, target_y, initial_x, max_reps=100, max_err=0.01):
    """
    Find a value for x such that fn(x) == target_y (+/- max_err)
    """
    x = initial_x
    for _ in range(max_reps):
        err = fn(x) - target_y
        if abs(err) <= max_err:
            # found a good enough solution
            return x
        else:
            # first-order correction to reduce error
            x -= err / diff(fn, x)
    raise ValueError("solver failed to converge")

def final_balance(fixed_payment, initial_balance, period_rate, num_periods):
    """
    Calculate the final balance on a fixed payment plan
    """
    balance = initial_balance
    for _ in range(num_periods):
        balance = (balance - fixed_payment) * (1. + period_rate)
    return balance

def round_up_to_next_cent(amt):
    return ceil(amt * 100.) / 100.

def main():
    initial_balance = 3329.
    annual_interest = 0.2

    # bind arguments to create a single-argument function to pass to newton_solver
    my_final_balance = partial(final_balance, initial_balance = initial_balance, period_rate = annual_interest / 12, num_periods = 12)

    # initial guess - in total you will pay about half a year's interest
    monthly_payment_guess = initial_balance * (1. + annual_interest * 0.5) / 12

    # solve to find accurate value
    monthly_payment = newton_solver(my_final_balance, 0., monthly_payment_guess)
    monthly_payment = round_up_to_next_cent(monthly_payment)

    # and report the result
    print("A fixed monthly payment of ${:0.2f} results in a final balance of ${:0.2f}".format(monthly_payment, my_final_balance(monthly_payment)))

if __name__ == "__main__":
    main()

which produces

A fixed monthly payment of $303.33 results in a final balance of $-0.07

add a flag variable to control if balance is changed. Another way could be to check if balance is changed. ( if balance != original_balance: ... ) eg

balance = 3329             # starting balance
annualInterestRate = 0.2   # yearly interest rate
minFixedPayment = 0        # initiate a minimum monthly payment of 0
change_flag = 0
while balance > 0:
    if change_flag: minFixedPayment = minFixedPayment + 10
    change_flag = 1
    for i in range(1,13,1):
        unpaidBalance = balance - minFixedPayment
        balance = unpaidBalance + ((annualInterestRate/12) * unpaidBalance)
        i=i+1
print(round(minFixedPayment,2))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM