问题描述
上周在一个外文学习网站上看到一个很不错的程序设计题目,描述比较冗长,整整一页全英文描述,对于英文理解能力要求较高,在此就不赘述了。读完本文需要一定的耐心,但是读懂会跟我一样受益匪浅的,题目大概意思是这样的:
1、一名刚毕业于麻省的学生获得一份工作,年薪为annual_salary,他希望3年之内能买房,房子首付target=250000。
2、此时他身无分文,他打算将每个月工资的固定比例拿出来存银行,这个比例用rate表示(10进制表 示,保留小数点后4位,比如0.0704就是7.04%,忽视7.041%与7.039%之间的这种误差)银行存款年利率为4%,附加解释:即每个月的钱将增加(4%)/12。
3、他的工资不是一成不变的,每过6个月,他的工资将会增加原来工资的7%
4、要求程序设计求出这个最低rate,题目给出了两组输入输出案例作为格式参考要求。
之所以说这个题目不错,首先在题目理解上相信就已经能给大部分人压力了(但其实外文网站上的题目都这样,有时光是一个赋值语句能搞定的事就扯了一大段专业术语,心理战术)其次一开始会比较无头绪,但是当看完我的解题后,相信你也会觉得这个题目很不错,因为它活跃了我们的思维,很多算法别人叫我们去写,我们或许能够写出来,但是遇到具体问题的时候未必能想到用正确而有效的算法,这道题就是一个不错的例子。
解题思路
step1:假设我们已知这个rate,我们求员工3年的存款,我们会怎么求?
step2:将step1中的已知rate,用个自变量x替代,那么我们就可以得到一个求他3年存款的函数,函数参数是自变量x,假设函数为func(x),我们只需要求使func(x)>250000成立的最小值,也就是func(x)-target>0的最小值。
step3:题目解读到step2相信很多人应该会开始想到用二分查找求函数零点的方式,因为很显然,rate越大,存款越多,func(x)绝对是一个连续的单调递增函数,而二分查找适用于有序的序列中查找。那么问题来了,rate肯定是一个(0,1】之间的小数,如何将一个连续的单调递增函数转化成一个离散的有序序列呢?如何用二分查找呢?
step4:要用二分查找,自变量的取值必然是整数,将(0,1】区间放大为原来的10000倍,为什么是10000倍呢,因为题目要求保留小数点后4位,并且忽视0.07041和0.07039之间的这种误差,加入我们放大100000倍的话,7041和7039之间必然还可以进行二分,所以此处我们放大10000倍,在区间【1,10000】之间进行二分查找,区间虽然看起来有点大,但是以二分查找log2(n)的效率,问题是不大的。
下面贴上整个解题代码(python3),已经过详加注释:
annual_salary=int(input("Enter the starting salary: "))
semi_annual_raise=0.07 #涨薪比例
target=250000 #存款目标金额
r=0.04 #年利率
inc=1+r/12 #本金增加倍数
s=1+semi_annual_raise #每月工资增加倍数
def func(x): #x表示工资中用于存款的比例
money=annual_salary*(x/120000) #年薪/12即为月薪,再乘以比例,注意x放大了10000倍
current_saving=money #一个月后存款
i=2
while i<37:
temp=current_saving*inc
if i%6==1: #每过6个月,工资增加
money*=s
current_saving=temp+money #i月后拥有存款
i=i+1
return current_saving-target
left=1
right=10000
if func(right)<0:
print("It is not possible to pay the down payment in three years.")
else: #二分查找求最低rate
counter=0
while left<right:
mid=int((left+right)/2)
counter+=1 #计算二分次数
if func(mid)<0:
left=mid+1
if func(mid)>0:
right=mid-1
rate=left/10000 #求的是rate的最小值,取左值
print("Best savings rate: ",rate)
print("Steps in bisection search: ",counter)
进行二分查找前,先进行了最值判断,因为如果比例为1还不能存够的话就没必要二分了,直接输出提示信息,当然这也是题目案例要求的。然后加了个counter计数器,来计算二分的次数,来证明一下它的效率还是很高的。
整个题目最大的收获就是一种思维的灵活跳跃,一步步的推理引导,灵活运用所学方法,妙用二分查找,用程序解决实际问题,测试案例有限,如有任何错误或者不合理之处,请读者随时指出,我是python的初学者,我将及时更正并万分感谢!