【问题标题】:MIT OCW 6.00's Chicken McNugget program... again麻省理工学院开放式课程 6.00 的麦乐鸡计划……再次
【发布时间】:2014-02-20 00:21:25
【问题描述】:

我花了一点时间阅读之前关于 MIT OpenCourseWare 6.00 的问题集的问题/答案,该问题集有关用于确定 6、9 和 20 包中的最大 无法达到 数量的麦乐鸡块的程序. 不幸的是,我无法将这些答案集成到我尝试自己编写的代码中。

首先,这是我尝试为其编写代码的问题集的实际措辞:

...我们可以编写一个详尽的搜索来找到最大数量的无法准确购买的 McNuggets。搜索的格式大概应该遵循这个大纲:
• 假设从 1 开始无法准确购买的 McNuggets 数量的可能实例
• 对于每个可能的实例,称为 n,测试是否存在非负整数 a、b 和 c,使得 6a+9b+20c = n。 (这可以通过查看 a、b 和 c 的所有可行组合来完成)
• 如果不是,n 不能精确数量购买,保存 n
• 当你找到六个连续的 n 值实际上通过了精确解的测试时,最后保存的答案(不是最后一个有解的 n 值)就是正确答案,因为你知道任何更大数量的定理也可以以精确的数量购买。 编写一个迭代程序,找出不能以确切数量购买的最大数量的 McNuggets。您的程序应按以下格式打印答案(其中提供了正确的数字代替 (n)):“不能以确切数量购买的最大数量的 McNuggets:(n)”
提示:您的程序应该按照上面的大纲。

(引用的定理基本上是说,一旦你连续有六个允许的 n 值,你可以无限期地添加六个,并且不会再有 n 的不允许值。)

这是我的代码:

n = 1                                  #set first value for trying to solve the equation
savedn = []                            #list for holding values of n w/ no integer solution
consecutivecount = 0
while consecutivecount < 6:
    for a in range(0,n):
        for b in range(0,n):
            for c in range(0,n):
                if 6*a + 9*b + 20*c == n:
                    consecutivecount += 1
                else:
                    #NOW A MIRACLE HAPPENS!!!
                    consecutivecount = 0      #resets consecutivecount value to zero
                    savedn += [n]             #adds current value of n to list
                n += 1                        #increases value of n to continue test loop
print 'Largest amount of McNuggets that cannot be bought in exact quantity:',str(savedn[-1])+'.'

我的困难:

  1. 如您所见,我被困在中间,指示的地方。我使用布尔值看到了这个问题的其他问题/答案,但我不确定为什么有人会这样做。不过,我真的 必须 使用 bool 吗?为什么?基本上我会很感激帮助找出为“其他”执行的有效操作。

  2. 我不确定我是否真的正确使用了已保存列表。当我运行这段代码的测试段时,我知道我显然在向列表中添加值,但是确认这是使用“savedn += [n]”的正确方法会很好。

    李>

我仍然知道很少的命令,并且我的数学技能非常生疏,所以对于我在本课程中的最后一个问题,请假设我是一个回答问题的白痴。另一方面,如果我在上面的尝试完全偏离了标准,请随时明确建议我应该如何从头开始。

感谢并道歉,这个问题的其他讨论似乎不够彻底,没有用。 (也很抱歉这需要 Python 2.5。)

【问题讨论】:

  • “使用布尔值”到底有什么问题?将“标志变量”设置为 True 或 False 以跟踪某些条件是真还是假是完全合理的做法。当然,几乎总有一些方法可以重组你的代码,这样就没有必要了,但这种重组通常会非常激烈。
  • 我不明白问题的文字......但这听起来有点酷,提示你知道a最多只能达到n//6......同样b到n//9和c只能到n//20 ... 这应该有助于解决速度问题
  • 回答 2. 是的,只要该行执行它就应该附加到 savedn
  • @abarnert 我对使用 bool 没有实际问题;我的麻烦是在我的研究中我还没有遇到过如何正确使用它们的解释,所以我一直在尝试使用我所知道的。如果编写此算法的最快/最优雅的方法涉及布尔值,我会全力以赴,但我想我首先会通过展示我创建的基本条件/迭代并找出如何使它们按原样工作来减少人们的负担.除非我已经开始了,否则实际上没有办法做到这一点。
  • @devonjones:“使用布尔值”是微不足道的。如果您知道如何使用 int(可以是任何数字的值),那么您就会知道如何使用 bool(可以是 True 或 False 的值,仅此而已)。在这里学习真的没有什么复杂的。事实上,您已经在您编写的每个 if 语句中正在使用布尔值:6*a + 9*b + 20*c == n 要么是 True,在这种情况下会发生“if”部分,要么是 False,在这种情况下会发生“else” " 部分发生了。

标签: python algorithm python-2.5


【解决方案1】:

你快到了。假设问题是“找到订购什么来获得'n' McNuggets 的解决方案”?您拥有其中的核心,并且可以通过以下方式解决该问题:

solution = (0,0,0)
for a in range(0,n):
    for b in range(0,n):
        for c in range(0,n):
            if 6*a + 9*b + 20*c == n:
                solution = (a,b,c)

现在,您需要做的就是像以前一样用簿记围绕它:

while consecutivecount < 6:
    solution = (0,0,0)
    # ** find a solution like above
    if solution == (0,0,0):
        consecutivecount = 0      #resets consecutivecount value to zero
        savedn += [n]             #adds current value of n to list
    else:
        consecutivecount += 1
    n += 1                        #increases value of n to continue test loop

因此,您为n 寻找解决方案,如果找到,则计算连续计数,如果未找到,则保存另一个无法达到的数字(并重置连续计数)。无论哪种情况,是时候继续另一个n了。

【讨论】:

  • 这似乎很有帮助。不幸的是,我的 IDLE 决定让我崩溃,我不想在这个线程中为这个问题提供帮助请求来困扰任何人,所以我稍后将不得不尝试这个。不过谢谢。
  • 作为一个教育练习,您可以将其更改为使用布尔值来记录您是否找到解决方案,并跳过记录实际解决方案整数(从未使用过)。
  • 这是 (a) 不正确的 - 你的范围有一个 off-by-1 错误,尽管这只会在你遇到包大小为 1 时才有意义 - 并且 (b) 非常低效。执行for a in range(n // 6 + 1): / for b in range((n - 6*a) // 9 + 1): / if (n - 6*a - 9*b) % 20 == 0: / return True 实际上减少了 3300 倍的迭代次数(265 次而不是 894,916 次)。
  • 我想我在这里有点掌握数学,@HughBothwell,但现在似乎没有定义 c。在b的for循环之后,我添加了for c in range ((n-6*a-9*b)//20 + 1),然后使用了if 6*a + 9*b + 20*c == n。现在我得到了答案“43”,这是我已经通过蛮力计算得出的正确答案!
  • @devonjones:不需要为 c 做另一个 for 循环; c 被隐式定义为(n - 6*a - 9*b) / 20,如果(n - 6*a - 9*b) % 20 == 0 则只有整数答案,因此您可以将最后一个子句读为if (c has an integer solution): return True。我们不需要知道c的,只要知道它存在即可。
【解决方案2】:

我使用了 itertools.product 来避免 for 循环的 3 深嵌套。这意味着我可以轻松跳出循环

from itertools import product, count

v = [6,9,20]
min_v = min(v)
miss = 0

for n in count(1):
    for w in product(range(n), repeat=len(v)):
        if sum(i * j for i, j in zip(v, w)) == n:
            break
    else:
        miss = n
    if n == miss + min_v:
        break

print miss

这确实测试了一系列不必要的案例,正如@Joran 在问题的 cmets 中所注意到的那样。以下方法通过稍微不同地设置product 来避免这些情况

from itertools import product, count

v = [6,9,20]
min_v = min(v)
miss = 0

for n in count(1):
    for w in product(*(range(1 + n // i) for i in v)):
        if sum(i * j for i, j in zip(v, w)) == n:
            break
    else:
        miss = n
    if n == miss + min_v:
        break

print miss

如果我们用step参数来范围,可以进一步简化成这样

from itertools import product, count

v = [6,9,20]
min_v = min(v)
miss = 0

for n in count(1):
    for w in product(*(range(0, n + 1, i) for i in v)):
        if sum(w) == n:
            break
    else:
        miss = n
    if n == miss + min_v:
        break

print miss

【讨论】:

  • 这是一个不错的答案,但我认为您无法向不愿了解 bool 的人解释 itertools.product、splat 运算符和生成器表达式.
  • 另外,我很确定这在 Python 2.5 中不起作用。一方面,itertools 中的大部分组合内容是在 2.6 中添加的。
  • 确定你做到了:sum(w) == n 是一个布尔值。也许if sum(w) - n:passelse:break? :)
  • 啊,你知道我的意思是“bool as a flag”
  • 我很乐意了解这些内容。它们完全超出了我开始编写原始代码时所学的范围。如果有人解释了所述代码的优点/缺点以及如何使用它,而不是把我自己没有开始的代码交给我,我仍然会很感激。如果这不是我从概念上理解“如何制作算法”的家庭作业——而是我为立即实际应用提出的问题——那么相信我,我会接受 gnibbler 的答案并按照它运行。
【解决方案3】:

这是一个通用版本,适用于任意数量的片段:

def solution_exists(amt, pieces, i=None):
    """
    Return True if any whole multiples of pieces[:i+1] sum to amt, else False
    """
    if i is None:
        i = len(pieces) - 1   # start with last item
    p = pieces[i]
    if i:
        return any(solution_exists(amt - p*k, pieces, i-1) for k in range(amt // p + 1))
    else:
        return amt % p == 0

def find_max_unbuyable(pieces):
    least = min(pieces)
    n = 0
    last_unsolved = None
    consecutive = 0
    while consecutive < least:
        n += 1
        if solution_exists(n, pieces):
            consecutive += 1
        else:
            last_unsolved = n
            consecutive = 0
    return last_unsolved

def main():
    pieces = [6, 9, 20]
    max_unbuyable = find_max_unbuyable(pieces)
    print("Largest number of McNuggets that cannot be bought in exact quantity: {n}".format(n=max_unbuyable))

if __name__=="__main__":
    main()

find_max_unbuyable 非常简单;从问题的描述来看,这几乎是一步一步来的。

solution_exists 参与度更高;这是一个递归的可满足性测试。对于每包,从最后一个开始到第一个,它会计算出可以购买的最大数量 (amt // p) 和剩余的购买数量 (amt - p*k)。然后它调用自己反对减少的子问题。如果子问题解决了,那么问题就解决了,any 缩短评估并返回 True。对于基本情况(i == 0,我们正在查看最后一包),如果剩余数量可被 p(amt % p == 0)整除,则返回 True。

这样写solution_exists有两个好处:首先,它是一个更有效的解决方案(迭代尽可能少,将最后一个部分作为模除法而不是另一个迭代,尽快短路找到了解决方案);其次,因为它是递归的,它会很高兴地对任意数量的包进行操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多