根据一些评论澄清,我可以提出一种解决方法。具体来说,这是我对可能非常棘手的问题的“尝试最简单的方法”。
首先,棘手的部分是所有餐点的总和必须在一定范围内,但 SQL 没有我知道的内置功能,它可以一次性完成您想要的。不过没关系,因为我们可以在 PHP 中实现这个功能。
因此,假设您要求 5 顿总热量为 2000 卡路里的膳食——为简单起见,我们将其他变量放在一边,但它们的工作方式相同。然后我们计算出“平均”膳食是 2000/5=400 卡路里,但显然任何一顿饭都可能超过或低于该数量。我不是营养师,但我假设您不想要超过平均膳食大小 1.25 倍至 2 倍的膳食,因此我们可以将初始查询限制在此数量。
$maxCalPerMeal = ($highCal / $countRecipes) * 1.5;
$mealPlanCaloriesRemaining = $highCal; # more on this one in a minute
然后,我们请求 1 顿小于 的随机餐$maxCalPerMeal
,并将其“保存”为我们的第一餐。然后我们从 中减去它的实际卡路里数$mealPlanCaloriesRemaining
。我们现在重新计算:
$maxCalPerMeal = ($highCal / $countRecipesRemaining) * 1.5); # 1.5 being a maximum deviation from average multiple
现在,下一个查询将询问小于$maxCalPerMeal
AND的随机餐,而不是$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
并且您将查询限制为大于此计算的最小值,每次循环重新计算,幸福自然会随之而来。
最后我们必须处理退化的情况——如果使用这种方法你最终需要一顿太小或太大而无法填满最后一个位置的食物怎么办?好吧,您可以通过多种方式处理此问题。这是我推荐的。
最简单的方法就是返回少于所需数量的饭菜,但这可能是不可接受的。你也可以吃一些特殊的低卡路里餐,由于最低的平均饮食含量,只有当有人真的不得不挤进一顿便餐才能使计划奏效时,它们才有可能被退回。我更喜欢这个解决方案。
第二个最简单的方法是扔掉你到目前为止的饮食计划,从头开始重新开始;这次它可能会起作用,也可能不会,所以你需要一个控制循环来确保你不会进入一个无限的工作密集型循环。
最不简单的是,需要再次进行控制循环最大迭代,但在这里您使用特定策略来尝试获得更可接受的膳食计划。在这种情况下,您选择超出您的饮食限制的最高价值的可选餐并将其扔掉,然后尝试提取较小的餐 - 也许不大于新计算的平均值。它可能会使计划成为一个整体,或者你可能会超过另一个计划的价值,迫使你回到一个可能无法解决的循环中——或者可能只需要几十次迭代就可以得到一个有效的。
虽然在写出来时这听起来很多,但即使是一台非常慢的计算机也应该能够每隔几秒就产生数十万个建议的膳食计划而不会暂停。即使您有数以百万计的食谱可供选择,您的数据库也不会受到太大压力,并且您返回的膳食计划将尽可能随机。通过简单的比较和另外一两次调用来生成额外的膳食计划,也可以很容易地确定多个建议的膳食计划不是重复的——不用担心明显的延迟!
通过以最小的数学开销将事情分解成小步骤,一项艰巨的任务变得可以管理 - 你甚至不需要数学学位来解决它:)
(顺便说一句,我认为您在那里建立了一个非常好的网站,所以不用担心!)