在PHP中的某些范围内创建固定长度的非重复排列

时间:2013-08-13 18:07:02

标签: php permutation

我有一张包含1000份食谱的餐桌,每份食谱都含有卡路里,蛋白质,碳水化合物和脂肪值。

我需要在PHP中找出一个算法,它允许我指定卡路里,蛋白质,碳水化合物和脂肪的值范围,以及规定每个排列中的食谱数量。类似的东西:

getPermutations($recipes, $lowCal, $highCal, $lowProt, $highProt, $lowCarb, $highCarb, $lowFat, $highFat, $countRecipes)

最终目标是允许用户输入当天的卡路里/蛋白质/碳水化合物/脂肪目标(例如,范围,1500-1600卡路里),以及他们想吃多少餐(计数)每套食谱中的食谱)并返回符合其目标的所有不同的膳食组合。

我之前通过填充每个可能组合的表格(请参阅:Best way to create Combination of records (Order does not matter, no repetition allowed) in mySQL tables)并使用范围限制查询它来尝试此操作,但事实证明这并不高效,因为我最终得到了数十亿条记录扫描并且需要无限期的时间。

我发现了一些接近我需要的排列算法,但是我没有找到卡路里/蛋白质/碳水化合物/脂肪的值范围限制(参见:Create fixed length non-repeating permutation of larger set)当谈到这种类型的逻辑/数学时,我现在处于亏损状态,所以任何帮助都很受欢迎。

1 个答案:

答案 0 :(得分:2)

根据一些评论澄清,我可以建议一种方法来解决它。具体来说,这是我“尝试可能最简单的事情”来处理可能非常棘手的问题。

首先,棘手的部分是所有餐食的总和必须在一定范围内,但SQL没有内置功能,我知道这特别是你想要的一次通过;但是没关系,因为我们可以在PHP中实现这个功能。

因此,假设您要求总共2000卡路里的5餐 - 为简单起见,我们将其他变量放在一边,但它们的工作方式相同。然后,我们计算出“平均”膳食是2000/5 = 400卡路里,但显然任何一餐都可能超过或低于该量。我不是营养师,但我认为你不会想要吃饭的平均膳食大小超过1.25倍-2倍,所以我们可以限制初始查询到这个数量。

$maxCalPerMeal = ($highCal / $countRecipes) * 1.5;
$mealPlanCaloriesRemaining = $highCal; # more on this one in a minute

然后我们要求一份低于$maxCalPerMeal的随机餐,并将其“保存”为我们的第一餐。然后我们从$mealPlanCaloriesRemaining中减去它的实际卡路里计数。我们现在重新计算:

$maxCalPerMeal = ($highCal / $countRecipesRemaining) * 1.5); # 1.5 being a maximum deviation from average multiple

现在,下一个查询将同时询问小于$maxCalPerMeal$mealPlanCaloriesRemaining的随机餐,而不是您已在此特定膳食计划选项中保存的其中一餐(从而确保唯一饭菜 - 早餐,午餐和晚餐都没有mac'n'cheese!)。我们会像上一个查询一样更新变量,直到结束。对于最后一餐请求它我们不关心平均值并且它关联多个,因为复合查询你将得到你想要的东西,并且不需要使控制循环复杂化。

假设5餐2000卡路里最大饮食最坏的情况:

1:600卡路里 用餐2:437 用餐3:381 用餐4:301 用餐5:281

或类似的东西,在大多数情况下,你会得到一些更好,更随机的东西。但在最坏的情况下它仍然有效!现在这实际上只适用于通常情况。添加更多的最大值,如脂肪和蛋白质等,很容易,所以让我们接下来处理低点。

我们需要做的就是支持“每天最低卡路里”,这样就增加了另一组平均值:

$minCalPerMeal = ($lowCal / $countRecipes) * .5 # this time our multiplier is less than one, as we allow for meals to be bigger than average we must allow them to be smaller as well

并且您将查询限制为大于此计算的最小值,重新计算每个循环,并且自然会随之而来。

最后我们必须处理堕落的情况 - 如果使用这种方法,你最终需要一个小的或太大的餐来填补最后一个插槽?好吧,你可以通过多种方式处理这个问题。这是我推荐的。

最简单的就是返回少于所需的膳食量,但这可能是不可接受的。您还可能有特殊的低卡路里餐,由于最低的平均膳食含量,如果有人真的不得不挤吃便餐以使计划有效,那么它们很可能会被退回。我更喜欢这个解决方案。

第二个最简单的就是抛弃你到目前为止的用餐计划并从头开始再生;它可能会工作,或者它可能不会,所以你需要一个控制循环来确保你不会进入一个无限的工作密集循环。

最不容易,需要再次进行控制循环最大迭代,但是在这里您使用特定策略来尝试获得更可接受的膳食计划。在这种情况下,您可以选择超出您饮食限制的最高值并将其扔出去,然后尝试拉一顿较小的一餐 - 也许是一个不大于新计算平均值的餐。它可能会使整个计划发挥作用,或者你可能会重新审视另一个计划的价值,迫使你回到一个无法解决的循环中 - 或者它可能需要几十次迭代才能得到一个有效的循环。

虽然这在写出来时听起来很多,但即使是非常慢的计算机也应该能够每隔几秒就制作成千上万的建议用餐计划而不会暂停。即使您有数以百万计的食谱可供选择,您的数据库也将承受很小的压力,您返回的膳食计划将随机而变化。通过简单的比较确定多个建议的膳食计划也不容易重复,并且可以生成额外的膳食计划的另一个或两个呼叫 - 不用担心明显的延迟!

通过以最小的数学开销将事情分解为小步骤,令人生畏的任务变得易于管理 - 您甚至不需要数学学位来解决这个问题:)

(顺便说一句,我认为你有一个非常好的网站,所以不用担心!)