简体   繁体   English

均分搜索以选择最佳储蓄率

[英]Bisect Search to choose best savings rate

Hi I would like some help with this questions set as one of the problems on the MIT OCW Computer Science and Python Course. 嗨,我希望获得有关此问题的帮助,这是MIT OCW计算机科学和Python课程中的问题之一。 I know people have asked similar questions and I have found useful posts such as Bisection search code doesnt work but I am still stuck! 我知道人们也问过类似的问题,但我发现有用的帖子(例如, Bisection搜索代码)不起作用,但我仍然被卡住了!

I have struggled with this problem for many days and tried to tackle it in different ways and failed in all ways. 我已经为这个问题苦苦挣扎了许多天,并试图以不同的方式来解决这个问题,但是在所有方面都以失败告终。 If at all possible, could somebody just hint at where I am going wrong, rather than telling me the answer. 如果可能的话,有人能暗示我要去哪里,而不是告诉我答案。 I would like to solve this problem for myself with bit of help. 我想通过一点帮助自己解决这个问题。

For reference, the question is part C, here: https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/assignments/MIT6_0001F16_ps1.pdf 作为参考,问题是C部分,在这里: https : //ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in -python下落-2016 /任务/ MIT6_0001F16_ps1.pdf

As I have been struggling, I have broken down this task into an overall aim and then into steps to solve the problem. 在努力的过程中,我将这项任务分解为一个总体目标,然后分解为解决问题的步骤。

Aim: try to find the best rate of savings to achieve a down payment on a $1M house in 36 months ##Steps to solve the problem: 目标:尝试找到最佳的储蓄率,以在36个月内实现首付100万美元房屋的首付。##解决问题的步骤:
1) make a guess on the savings rate, which is the average of the 0 and 1000 1)猜测储蓄率,即0和1000的平均值
2) calculate how that would grow over 36 months 2)计算36个月后的增长情况
3a) if amount reached goes over 25% of $1m within 36 months, then a lower savings rate should be the new guess 3a)如果在36个月内达到的金额超过100万美元的25%,那么较低的储蓄率应该是新的猜测
...max=guess (old guess) and min=0 and update the guess (average of high and low) ... max =猜测(旧的猜测)和min = 0并更新猜测(高和低的平均值)
...run the calculation in step 2 with the new guess ...使用新的猜测运行步骤2中的计算
3b) if amount does not reach 25% of $1m within 36 months, then a higher savings rate should be the new guess 3b)如果金额在36个月内未达到100万美元的25%,则应以更高的储蓄率作为新的猜测
...min=guess (old guess) and update the guess (average of high and low) ...run the calculation in step 2 with the new guess ... min = guess(旧的猜测)并更新猜测(高和低的平均值)...使用新的猜测运行步骤2中的计算
3c) if amount reaches 25% of $1m within the 36th month deadline then quit and record the savings rate as the best guess. 3c)如果金额在第36个月的截止日期内达到100万美元的25%,则退出并记录储蓄率作为最佳猜测。
For simplicity: assume no interest and assume wages remain same 为简单起见:假设没有利息,并且假设工资保持不变

So here's my code with the current effort at solving this. 所以这是我目前正在努力解决此问题的代码。 (it leads to the "guess" variable tending to 0 and then infinitely looping) (这导致“猜测”变量趋向于0,然后无限循环)

total_cost=1000000 #cost of house
portion_down_payment=0.25 #fraction of cost needed for downpayment on house
downpayment=total_cost*portion_down_payment

starting_annual_salary=float(input("Enter the starting salary: "))
low=0
high=1000
bisect_steps=0
month=1 #set as 1 because the first calculation will occur in month 1 
guess=(low+high)//2
current_savings=0

def calSavings(current_savings,monthly_salary,guess,month):
    while month<37:
        monthly_savings=monthly_salary*(guess/1000)
        current_savings+=monthly_savings
        month+=1
    return(current_savings) 

current_savings=calSavings(current_savings,monthly_salary,guess,1)
while True:
    current_savings=calSavings(current_savings,monthly_salary,guess,1)
    if current_savings>downpayment and month<=35: #if amount reached goes over 25% of $1m within 36 months
        #a lower savings rate should be the new guess
        high=guess #max=guess (old guess) and min=0 and update the guess
        bisect_steps+=1
        guess=(low+high)//2
        print("The guess was too high, so the new lower guess is",guess," This is bisect step",bisect_steps)
        continue #send new guess up to beginning of while loop to calculate 
    elif current_savings<downpayment and month>=36: #if amount does not reach 25% of $1m within 36 months
        low=guess
        bisect_steps=+1
        guess=(low+high)//2
        print("The guess was too low, so the new higher guess is",guess," This is bisect step",bisect_steps)
        continue #send new guess up to beginning of while loop to calculate 
    elif current_savings>=downpayment and month==36: #if amount reaches 25% of $1m in the 36th months then quit
        # record the savings rate as the best guess
        print("The best savings rate is ",guess/100,"%, the amount saved was ",current_savings," in ",month," months")
        break #break out of while loop

I know other people have asked similar questions (I have looked at those answers and still not solved my problem) but more than an answer I want help on HOW to solve this problem. 我知道其他人也曾问过类似的问题(我已经看过这些答案,但仍然没有解决我的问题),但我想获得的更多帮助是如何解决此问题的帮助。

Update 更新

The reason why your loop isn't stopping is because you aren't giving it enough time. 循环没有停止的原因是因为您没有给它足够的时间。 What you are forgetting is that you're dealing with the decimal type. 您忘记的是您正在处理decimal类型。 Using the == with decimal values is always dangerous. ==decimal值一起使用总是很危险的。 The decimal type is accurate (by default) to 28 places, which means you're trying to find an extremely good approximation for this problem, because only when it's correct to 28 decimals will (current_savings>downpayment or current_savings<downpayment) evaluate to False invoking your exit condition. decimal类型准确(默认情况下)为28个位,这意味着您正尝试找到一个非常好的近似值,因为只有当它正确为28个小数时, (current_savings>downpayment or current_savings<downpayment)评估结果才为False调用您的退出条件。

Basically, the issue that's causing your problem is that even when you eventually get the estimate of $1,000,000.0000000001, python says this is not equal to $1,000,000.0000000000, so it keeps going until it gets the next 0, but then it just adds another zero and so on. 基本上,导致您出现问题的问题是,即使您最终获得的估算值为$ 1,000,000.0000000001,python表示这也不等于$ 1,000,000.0000000000,因此它一直持续到得到下一个0,然后才添加另一个零,依此类推。 。 This will continue for a very very long time and in rare cases might never stop due to the fact that not all decimal numbers can be stored as binary numbers (1,000,000 is not among those cases). 这将持续非常长的时间,在极少数情况下,由于并非所有十进制数字都可以存储为二进制数(1,000,000不在这种情况下),因此可能永远不会停止。

So, how do we solve this? 那么,我们该如何解决呢? There are two options. 有两种选择。 The easiest one is to ignore cents and just cast your comparison values to int , this will make sure that any value off by a fraction of a dollar will be accepted. 最简单的方法是忽略美分,只将比较值转换为int ,这将确保接受少于一美元的任何值。 The other options is to create a range of accepted answers. 其他选项是创建一系列可接受的答案。 Say for example, I'd like to save EXACTLY $1 million in those 36 months, but that isn't likely to happen. 举例来说,在这36个月中,我想节省100万美元,但这不太可能实现。 So, instead, I'll settle for any amount in the range $1,000,000.00 - $1,000,010.00 (for example). 因此,我将以$ 1,000,000.00-$ 1,000,010.00范围内的任何金额结算(例如)。 This way, we ensure that any guess that is way too high will get rejected and only a very limited amount of guesses are accepted. 这样,我们确保任何过高的猜测都将被拒绝,并且仅接受非常有限的猜测。

Regardless of which route you go, it is generally a good idea to put the exit condition of an infinite loop to the top, this way you guarantee that it will always be evaluated. 无论走哪条路线,通常最好将无限循环的退出条件放在顶部,这样可以保证始终对它进行评估。

My suggestion would be to write a function like this and use that for your condition to exit the loop (which you would place at the top): 我的建议是编写一个这样的函数,并使用该函数使条件退出循环(将其放置在顶部):

def are_decimals_equal(a, b):
    accuracy = 0.0001
    return abs(a-b) < accuracy

This will consider 0.00009 (and all decimals less than that) to be equal to 0.0000. 这将认为0.00009(以及所有小于该数值的小数)等于0.0000。

Original 原版的

First off, just as a note, what you're doing is not called bisection, it's called binary search. 首先,请注意,您所做的不是二分法,而是二元搜索。

Now to the problem, you are never altering the value of month in your main loop. 现在解决问题,您永远不会在主循环中更改month的值。 This means, as soon as current_savings>downpayment evaluates to False, your program will go into an infinite loop as none of the conditions after it could evaluate to True as month>=36 will always be False. 这意味着,只要current_savings>downpayment评估结果为False,您的程序便会进入无限循环,因为在month>=36的情况下,该程序在任何条件下都可以评估为True时始终为False。

From what I can see, the second part of your conditions in the if/elif statements is unnecessary, your calSavings will always compute 36 months worth of savings, never more, never less. 据我所知,if / elif语句中条件的第二部分是不必要的,您的calSavings总是可以计算出36个月的节省额,永远不会多,也永远不会少。 Thus, if you remove that condition from your if/elif statements, your program will eventually stop and at that point it should settle on the correct answer. 因此,如果您从if / elif语句中删除该条件,则您的程序最终将停止,并且此时应确定正确的答案。

Lastly, the reason why you're seeing 0 as the output is your division at the end. 最后,您看到0作为输出的原因是最后的除法。 If you do print(typeof(guess)) you will see it is an integer, 100 is also an integer, thus this division will result in some value like 0.3123 which will get truncated to 0 . 如果您执行print(typeof(guess))您会看到它是一个整数,100也是一个整数,因此该除法将导致某些值(如0.3123将被截断为0 Change your output to float(guess/100) and this will go away. 将您的输出更改为float(guess/100) ,这将消失。

I hope it's okay for me to provide an answer to my own question here - though it's not a perfect answer. 我希望我可以在这里回答自己的问题,尽管这不是一个完美的答案。

The results the code produces seem plausible. 代码产生的结果似乎是合理的。

total_cost=1000000 #cost of house

portion_down_payment=0.25 #fraction of cost needed for downpayment on house
downpayment=total_cost*portion_down_payment

starting_annual_salary=float(input("Enter the starting salary: "))
monthly_salary=starting_annual_salary/12
low=0
high=1000
binary=0
month=1 #set as 1 because the first calculation will occur in month 1
guess=(low+high)//2
current_savings=0
tolerance=500

def calSavings(current_savings,monthly_salary,guess,month):
    while month<37:
        monthly_savings=int(monthly_salary*(guess/1000))
        current_savings+=monthly_savings
        month+=1
    return(current_savings)

current_savings=calSavings(current_savings,monthly_salary,guess,1)

while True:
    if abs(current_savings-downpayment)<=tolerance: #if the difference between the current savings and downpayment is less than $500
        # record the savings rate as the best guess
        print("The best savings rate is ",guess/10,"%, the amount saved was $",current_savings," in 36 months")
        break #break out of while loop
    elif (current_savings-downpayment)>tolerance: #if amount reached goes over 25% of $1m within 36 months
        #a lower savings rate should be the new guess
        high=guess #high=guess (old guess) and low=low (stays same) and update the guess
        binary=binary+1
        guess=(low+high)//2
        print("The guess was too high, so the new lower savings rate is",guess/10,"%. This is binary-search step",binary)
        current_savings=calSavings(0,monthly_salary,guess,1)
        continue #send new guess up to beginning of while loop to check if conditionals
    elif (downpayment-current_savings)>tolerance: #if amount does not come to within tolerance amount of 25% of $1m within 36 months
        low=guess #to make the guess higher, make low=guess (old guess) and high stay the same
        binary=binary+1
        guess=(low+high)//2
        print("guess is ",guess)
        if guess>=990: #check if the savings rate guess is getting too high
            print("Your wages are too low. You can't save up enough")
            break #exit the while loop because conditions will never be met
        print("The guess was too low, so the new higher savings rate is",guess/10,"%. This is binary-search step",binary)
        current_savings=calSavings(0,monthly_salary,guess,1)
        continue #send new guess up to beginning of while loop to check over the conditionals

The tolerance for an acceptable answer is within $500, but if I lower that to $50, I end up in a seemingly infinite loop again, where the guess and the low end up being the same. 可接受的答案的公差在500美元以内,但是如果我将其降低到50美元,我将再次陷入看似无限的循环,猜测和低端的结果相同。 I am happy that I've made some apparent progress, but baffled that I can't lower the tolerance without it going haywire again. 我很高兴自己已经取得了一些明显的进步,但是对不能再忍受它无法降低容忍度感到困惑。

BTW, I didn't want to seem like I ignored Nick's comments about making the variables into floats, but I explained why I worked in integers in my comment - does that seem correct? 顺便说一句,我不想​​我似乎忽略了尼克关于将变量转换为浮点数的评论,但是我解释了为什么我在评论中使用整数-看起来正确吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Python bisect:搜索值的范围 - Python bisect: search range of values Python 2.7中的Unicode字符的二进制搜索(二分法) - Binary search (bisect) for a Unicode character in Python 2.7 Python Bisect搜索-导致失败的abs() - Python Bisect Search- abs() causing failure 如何使用bisect在需要计算的数组中进行搜索 - How to use bisect to search in a compute-when-need array 选择三列中最好的 - Choose the best of three columns bisect.bisect 与 Python 中的 bisect.bisect_right 不同吗? - Is bisect.bisect different from bisect.bisect_right in Python? 选择*最佳*带宽进行小波变换 - Choose the *best* bandwidth for wavelet transformation 为什么 python 的“二分法”模块(二进制搜索)不允许将其与特定的“键”一起使用? - Why python's 'bisect' module (binary search) does not allow use it with particular 'key'? 使用 VowpalWabbit 优化 CTR(点击率):如何选择合适的参数? - Optimizing CTR (click through rate) with VowpalWabbit: How to choose suitable parameters? 在 dict() 上使用 `bisect_left()` 会引发 `dict is not a sequence` 错误。 如何遍历 dict() 以改进搜索过程时间 - Using `bisect_left()` on dict() raising `dict is not a sequence` error. How to traverse a dict() to improve search process time
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM