2

我有一张表,里面有 1000 个食谱,每个食谱都有与之相关的卡路里、蛋白质、碳水化合物和脂肪值。

我需要在 PHP 中找出一个算法,它可以让我指定卡路里、蛋白质、碳水化合物和脂肪的值范围,并规定每个排列中的食谱数量。就像是:

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

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

我之前尝试过用每个可能的组合填充一个表(请参阅:在 mySQL 表中创建记录组合的最佳方法(顺序无关,不允许重复))并使用范围限制查询它,但事实证明不是高效,因为我最终要扫描数十亿条记录,并且需要无限期的时间。

我找到了一些接近我需要的排列算法,但没有我正在寻找的卡路里/蛋白质/碳水化合物/脂肪的值范围限制(请参阅:创建较大的固定长度非重复排列set ) 当涉及到这种类型的逻辑/数学时,我现在不知所措,所以非常感谢任何帮助。

4

1 回答 1

2

根据一些评论澄清,我可以提出一种解决方法。具体来说,这是我对可能非常棘手的问题的“尝试最简单的方法”。

首先,棘手的部分是所有餐点的总和必须在一定范围内,但 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

现在,下一个查询将询问小于$maxCalPerMealAND的随机餐,而不是$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

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

最后我们必须处理退化的情况——如果使用这种方法你最终需要一顿太小或太大而无法填满最后一个位置的食物怎么办?好吧,您可以通过多种方式处理此问题。这是我推荐的。

最简单的方法就是返回少于所需数量的饭菜,但这可能是不可接受的。你也可以吃一些特殊的低卡路里餐,由于最低的平均饮食含量,只有当有人真的不得不挤进一顿便餐才能使计划奏效时,它们才有可能被退回。我更喜欢这个解决方案。

第二个最简单的方法是扔掉你到目前为止的饮食计划,从头开始重新开始;这次它可能会起作用,也可能不会,所以你需要一个控制循环来确保你不会进入一个无限的工作密集型循环。

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

虽然在写出来时这听起来很多,但即使是一台非常慢的计算机也应该能够每隔几秒就产生数十万个建议的膳食计划而不会暂停。即使您有数以百万计的食谱可供选择,您的数据库也不会受到太大压力,并且您返回的膳食计划将尽可能随机。通过简单的比较和另外一两次调用来生成额外的膳食计划,也可以很容易地确定多个建议的膳食计划不是重复的——不用担心明显的延迟!

通过以最小的数学开销将事情分解成小步骤,一项艰巨的任务变得可以管理 - 你甚至不需要数学学位来解决它:)

(顺便说一句,我认为您在那里建立了一个非常好的网站,所以不用担心!)

于 2013-08-13T20:11:38.713 回答