需要一种算法方法来计算膳食计划

时间:2014-06-26 03:40:09

标签: algorithm optimization combinations

我在解决一个看似简单的问题时遇到了麻烦。我的女朋友和我正在努力制定每周的膳食计划,我有这个绝妙的想法,我可以优化我们购买的东西,以便最大化我们可以用它做的事情。问题是,问题并不像看起来那么容易。简而言之,这是问题陈述:

问题: 鉴于100种成分的清单以及由100种成分中的一种或多种组成的50种菜肴的清单,找到可以产生最多菜肴的32种成分的清单。

这个问题看似简单,但我发现计算答案并非易事。我采用的方法是,我计算了32种成分的组合,作为100位字符串,其中32位设置。然后我检查一下该成分编号可以制作什么菜。如果菜肴数量大于当前最大值,我将保存清单。然后我计算下一个有效成分组合并重复,重复和重复。

32种成分的组合数量惊人!我看到它的方式,用我的方法计算需要大约300万亿年。我已经优化了代码,因此每个组合只花了75微秒才能搞清楚。假设我可以优化代码,我可以将运行时间缩短到仅仅几万亿年。

我认为有一种全新的方法。我目前正在XOJO(REALbasic)中对此进行编码,但我认为真正的问题在于方法而不是具体实现。有人想知道本世纪有可能完成的方法吗?

谢谢,

罗恩

4 个答案:

答案 0 :(得分:5)

mcdowella的分支和绑定解决方案将比详尽的枚举有很大改进,但它可能仍需要几千年。这是ILP solver最好解决的问题。

假设用餐i的成分组由R [i] = {R [i] [1],R [i] [2],...,R [i] [| R [i]给出] |]},您可以按如下方式对问题进行编码:

  • 为每个成分创建一个整数变量x [i] 1< = i< = 100.这些变量中的每一个都应该被约束到范围[0,1]。
  • 为每餐1< = i< = 50创建一个整数变量y [i]。这些变量中的每一个都应该被约束到[0,1]范围。
  • 对于每餐i,创建| R [i] |对于1< = j< = | R [i] |,形式y [i]< = x [R [i] [j]]的附加约束。这些将保证我们只能将y [i]设置为1,如果已包含所有膳食成分。
  • 添加一个约束,即所有x [i]的总和必须<= 32。
  • 最后,目标函数应该是所有y [i]的总和,我们应该尝试最大化它。

解决这个问题会产生所有变量的赋值x [i]:1表示应该包含成分,0表示不应该包含。

我的感觉是像CPLEX或Gurobi这样的商业ILP求解器可能会在几毫秒内解决这个150变量的ILP问题。即使是免费提供的解决方案,例如lp_solve,它通常很多更慢,应该没有问题。在不太可能的情况下,它似乎永远需要,你仍然可以解决LP放松,这将非常快(毫秒),并将给你(a)可以准备的最大膳食数量的上限( b)&#34;提示&#34;在变量值中:虽然x [i]通常不会精确地为0或1,但接近1的值表示应包含的成分,而接近0的值表示无用的成分。

答案 1 :(得分:2)

对此会有一个http://en.wikipedia.org/wiki/Branch_and_bound解决方案,但要获得确切的答案可能太昂贵了 - j_random_hacker建议的ILP可能更好 - LP松弛可能是一个比放松更好的启发式这里提出,ILP求解器将进行大量优化。

基本思想是您对部分解决方案树进行递归深度优先搜索,一次扩展一个。一旦你进行足够的递归以达到完全填充的解决方案,你就可以开始跟踪到目前为止找到的最佳解决方案。如果我标记你的成分A,B,C,D ...部分解决方案是长度<= 32的成分列表。从零长度解决方案开始,然后当你访问部分解决方案时,例如ABC您考虑ABCD,ABCE,......等等,并可能访问其中一些。

对于每个部分解决方案,您可以计算出该解决方案的任何后代可以达到的最高分数。准确了解这一点非常重要。这是一个建议 - 假设您有一个长度为20的部分解决方案。这样就可以选择12种成分,因此您可能做的最好的事情就是制作所需的不超过12种成分的菜肴。到目前为止,确定了有多少这些,这是部分解决方案的任何后代的最佳分数的一个例子。

现在,当您考虑将部分解决方案ABC扩展到ABCD或ABCE或ABCF时...如果您找到了迄今为​​止找到的最佳解决方案,那么您可以忽略目前为止无法获得超过最佳解决方案的所有扩展 - 这意味着你不需要考虑32种成分的所有可能组合。

一旦你找出了哪些可能的扩展可能包含一个新的最佳答案,你的递归搜索应该继续这些可能的扩展中最有希望的,因为这是最有可能存活的到目前为止找到了更好的解决方案。

实现这一目标的一种方法是巧妙地对其进行编码,以便上下递归意味着只对现有数据结构进行微小的更改,而这些更改通常会在向上和反向的过程中进行。

另一种方法是偷工减料。一个显而易见的方法是在时间不足时停止并找到目前为止在该阶段找到的最佳解决方案。另一种方法是更积极地丢弃部分解决方案。如果你得分到目前为止,例如100你可以丢弃那些得分不高于110的部分解决方案。这加快了搜索速度,你知道虽然你可能有最好的答案而不是100,不管你错过了什么,都不会比110更好。

答案 2 :(得分:1)

解决一些离散的数学问题?好here是维基。

您还没有考虑任何数量问题。例如,面粉将用于许多油炸食谱,但购买10磅面粉可能不是很好。对于您的解决方案需要的某些成分,成本可能过高。更不用说很多成分都在一切。 (牛奶,水,盐,胡椒,糖类等)

实际上,可能没有必要对这种程度进行优化。但我不会就SO提供关系建议。

至于新的解决方案:

我建议你找出很多你想要制作的东西,然后编写一个程序来建议其他东西。

答案 3 :(得分:1)

为什么不按照使用的菜肴数量来订购配料清单?

当然,这更像是一种贪婪的解决方案,但它应该为您提供一些关于最常用的成分的线索。从那里你可以编制一份可以用前30种(或其他)成分烹饪的菜肴清单。 此外,您可以按照缺少的成分数量订购剩余(不可烹饪)菜肴的清单,也可以尝试对其进行优化以最大化可烹饪菜肴的数量。

为了更多&#34;算法&#34;,我认为本地搜索在这里最有希望。从候选解决方案(对32种成分的随机分配)开始,并计算可烹饪菜肴的数量作为健身功能。然后检查邻近状态(切换一种成分)并移动到具有最高值的状态。重复直到达到最大值。经常这样做,你应该找到一个很好的解决方案。 (这将是一个简单的贪婪爬山算法)

有很多本地搜索算法,你应该能够在网上找到足够多的信息。大多数情况下,你不会找到最佳解决方案(当然这取决于问题),但仍然是一个非常好的解决方案。