Python:使用递归查找10的倍数

时间:2017-11-19 08:57:37

标签: python recursion

每次提交时,我都会在edX.org上的Python课程中获得11或12的正确答案,但是在讨论中没有得到任何人的帮助,因为没有人可以在那里发布任何代码(不是很有帮助)和似乎没有任何可用的支持人员可以从课程中与我交谈,我会为此付费,所以我在这里发帖。我正要付钱给某人来指导我,但现在没有人可以使用,而且我有一些压力要在12月之前为我的工作完成这门课程。

这是作业:

  

现在编写一个程序,计算所需的最低固定月付款,以便在12个月内偿还信用卡余额。按固定的每月付款,我们指的是每个月不变的单个数字,而是每月支付的固定金额。

     

在这个问题上,我们不会处理最低月付款率。

     

以下变量包含如下所述的值:

     

余额 - 信用卡上的未结余额

     

annualInterestRate - 年度利率小数

     

该计划应打印出一行:每月支付的最低金额,以偿还1年内的所有债务,例如:

     

最低付款:180
  假设根据月末的余额(在该月的付款之后)每月复利。每月付款必须是10美元的倍数,并且所有月份都相同。请注意,使用此付款方案可能会使余额变为负数,这是可以的。所需数学的摘要如下:

     

每月利率=(年利率)/ 12.0
  每月未付余额=(以前的余额) - (最低固定月付款额)
  每月更新余额=(每月未付余额)+(每月利率x每月未付余额)

这是我的代码:

#! /usr/bin/python3.6

from math import ceil

def roundup(x):
    return int(ceil(x / 10.0) * 10)

def getFixedPayment(balance,annualInterestRate,counter=12):

    totalLoan = balance + (balance*annualInterestRate)
    monthlyPayment = roundup(totalLoan/12.0)
    newBalance = totalLoan - monthlyPayment
    if counter < 12:
        newPayment = newBalance / counter + 1
    else:
        newPayment = newBalance / counter

    if counter == 1:
        return roundup(newPayment/12)
    else:
        return getFixedPayment(balance,annualInterestRate,counter-1)


#balance = 3329
#annualInterestRate = 0.2
print('Lowest Payment: ' + str(getFixedPayment(balance,annualInterestRate)))

以下是测试结果:(我这里所有15个,所以你可能能够识别出我无法看到的模式。标记为&#34; ERROR&#34;的那些是我错误的模式)

Test Case 1
balance = 3329; annualInterestRate = 0.2
Output:
Lowest Payment: 310

Test Case 2
balance = 4773; annualInterestRate = 0.2
Output:
Lowest Payment: 440

Test Case 3
balance = 3926; annualInterestRate = 0.2
Output:
Lowest Payment: 360

Randomized Test Case 1
balance = 265; annualInterestRate = 0.18
Output:
Lowest Payment: 30

Randomized Test Case 2
balance = 263; annualInterestRate = 0.18
Output:
Lowest Payment: 30

Randomized Test Case 3
balance = 317; annualInterestRate = 0.25
Output:
Lowest Payment: 30

Randomized Test Case 4
balance = 720; annualInterestRate = 0.2
Output:
Lowest Payment: 70

Randomized Test Case 5
balance = 4284; annualInterestRate = 0.2
Output:
Lowest Payment: 400

Randomized Test Case 6
balance = 3834; annualInterestRate = 0.15
Your output:
Lowest Payment: 340


*** ERROR: Expected Lowest Payment: 350

, but got Lowest Payment: 340

 ***
Correct output:
Lowest Payment: 350

Randomized Test Case 7
balance = 3045; annualInterestRate = 0.18
Output:
Lowest Payment: 280

Randomized Test Case 8
balance = 4461; annualInterestRate = 0.2
Output:
Lowest Payment: 410

Randomized Test Case 9
balance = 4657; annualInterestRate = 0.04
Your output:
Lowest Payment: 370


*** ERROR: Expected Lowest Payment: 400

, but got Lowest Payment: 370

 ***
Correct output:
Lowest Payment: 400

Randomized Test Case 10
balance = 3395; annualInterestRate = 0.2
Your output:
Lowest Payment: 320


*** ERROR: Expected Lowest Payment: 310

, but got Lowest Payment: 320

 ***
Correct output:
Lowest Payment: 310

Randomized Test Case 11
balance = 4045; annualInterestRate = 0.15
Your output:
Lowest Payment: 360


*** ERROR: Expected Lowest Payment: 370

, but got Lowest Payment: 360

 ***
Correct output:
Lowest Payment: 370

Randomized Test Case 12
balance = 3963; annualInterestRate = 0.18
Output:
Lowest Payment: 360

2 个答案:

答案 0 :(得分:3)

虽然函数是递归的(它自称), 它以毫无意义,无效的方式这样做。

考虑大于1时counter的任何值会发生什么:

def getFixedPayment(balance, annualInterestRate, counter=12):
    totalLoan = balance + (balance*annualInterestRate)
    monthlyPayment = roundup(totalLoan/12.0)
    newBalance = totalLoan - monthlyPayment

    if counter < 12:
        newPayment = newBalance / counter + 1
    else:
        newPayment = newBalance / counter

    if counter == 1:
        return roundup(newPayment/12)
    else:
        return getFixedPayment(balance,annualInterestRate,counter-1)
        #                                                 ^^^^^^^^^
        #                                                 the only change!

counter > 1时,函数“进行一些计算”, 但这没关系,因为最后它只是用counter - 1调用自己。 因此,对于起始值counter = 12, 该功能将反复调用自己11次无所事事。 因此,它减少到这个:

def getFixedPayment(balance, annualInterestRate):
    totalLoan = balance + (balance*annualInterestRate)
    monthlyPayment = roundup(totalLoan/12.0)
    newBalance = totalLoan - monthlyPayment

    newPayment = newBalance / counter + 1

    return roundup(newPayment/12)

这可能给出正确答案吗?不太可能。

考虑还款是如何运作的。 让我们举一个简单的例子,通过3个步骤来回报一些事情, 并考虑这种替代符号,因此写起来更简单:

  • 我们打算T支付今天支付的总金额
  • 让我们称r年利率的乘数,即4%的年利率,这个值将是1.04,因此我们可以通过{{1}获得需要支付的金额}。
  • 让我们致电T * r目标固定月付款

我们本月支付x后还需要多少钱来偿还?

x

也就是说,我们本月支付(T - x) * r ,剩余金额为x, 根据描述,我们需要乘以年利率。

下个月,我们再次支付T - x,所以还剩下的将是:

x

在第三个月,我们最后一次付款((T - x) * r - x) * r

练习的目标是找到x,以便:

x

让我们重新组织这个等式来找到((T - x) * r - x) * r - x <= 0

的值
x

在这个例子中,我们在3个月内偿还。 您可以通过添加一个月来了解此公式的变化:

((T - x) * r - x) * r <= x

(T - x) * r - x <= x / r

(T - x) * r <= x / r + x

T - x <= x / r / r + x / r

T <= x / r / r + x / r + x

T <= x * (1 / r / r + 1 / r + 1)

T / (1 / r / r + 1 / r + 1) <= x

也就是说,给定T / (1 / r / r / r + 1 / r / r + 1 / r + 1) <= x ,添加一个月意味着T / m

现在看起来像我们可以使用的递归逻辑。

没有完全破坏你的运动, 这是解决方案的模板,您只需要找出T / (m / r + 1)的正确值。祝你好运!

...

以下是一些验证您的解决方案的doctests。 如果您的程序位于名为def getFixedPayment(balance, annual_interest_rate, counter=12, interest=...): if counter == 1: return roundup(balance / interest) monthly_interest_rate = annual_interest_rate / 12 r = 1 + monthly_interest_rate return getFixedPayment(balance, annual_interest_rate, counter - 1, ...) 的文件中, 您可以使用calc.py运行doctests。 它将打印失败的测试用例的摘要,如果有的话, 或者如果一切都好的话,它什么都不打印。

python -mdoctest calc.py

答案 1 :(得分:2)

一个问题是:

  

假设利息每月复利

但你计算:

totalLoan = balance + (balance*annualInterestRate)

您不能以这种方式计算totalLoan。您应该考虑compound interest,并计算每个月的未付余额。

天真的方法

这是一个例子。请注意,您的递归可以用简单的循环替换:

def balance_after_a_year(balance, monthly_payment, annual_interest_rate):
    monthly_interest_rate = annual_interest_rate / 12.0
    for month in range(12):
        balance = (balance - monthly_payment) * (1 + monthly_interest_rate)
    return balance

print(balance_after_a_year(3834, 340, 0.15))
# 23.153402858591026
print(balance_after_a_year(3834, 350, 0.15))
# -107.05775649703746

您现在可以以天真的方式使用此功能。只需以10为步长从0迭代到balance,然后选择一年后余额为负数的第一个值:

def getFixedPayment(b,r):
    return next(m for m in range(0, b, 10) if balance_after_a_year(b, m, r) <= 0)
print(getFixedPayment(3834, 0.15))
# 350

此方法已通过@janos提供的doctest验证。

有更有效的方法,但这个方法简单明了。

每月付款公式

您可以使用monthly payment formula直接计算付款:

def getFixedPayment(b,r):
    m = r / 12.0
    return m * b / (1 - (1 + m)**-12)
print(getFixedPayment(3834, 0.15))
# 346.05036953133276

不需要任何循环!