饮食计划逻辑,匹配最佳结果

时间:2011-10-21 10:21:57

标签: logic

我有以下问题。我想用php& amp做一个饮食计划MySQL的。 我有以下内容:

  • 面包:1克蛋白质,2克碳水化合物,4克脂肪
  • 糖:3克蛋白质,6克碳水化合物,1克脂肪
  • 咖啡:8克蛋白质,2克碳水化合物,2克脂肪
  • 肉:7克蛋白质,0克碳水化合物,12克脂肪
  • 牛奶:16克蛋白质,12克碳水化合物,2克脂肪

有了上述内容,我想找到上述内容的最佳组合以匹配以下总数:

目标:160克蛋白质 - 41克碳水化合物 - 120克脂肪。

显示结果如:5块肉,3块牛奶等

我没有php和amp;的问题MySQL的。我试图找到这个问题背后的逻辑。

3 个答案:

答案 0 :(得分:3)

不是微不足道的。获取有关Dynamic Programming的书籍或好网页,特别是Knapsack problem

答案 1 :(得分:1)

这是一个应该有效的蛮力解决方案。这是一个SQL脚本(用SQL Server编写,应该在MySql中工作,但可能需要稍作修改),它会在找到最佳解决方案之前迭代所有可能的项目组合。

-- Limits by protein/carb/fat
DECLARE @protein_limit INT
SET @protein_limit = 160

DECLARE @carb_limit INT
SET @carb_limit = 90

DECLARE @fat_limit INT
SET @fat_limit = 120



-- Table holding valid items
DECLARE @items TABLE
(
    id INT IDENTITY(1,1),
    name VARCHAR(50),
    protein INT,
    carb INT,
    fat INT
)

INSERT INTO @items
      SELECT 'Bread', 1, 2, 4
UNION SELECT 'Sugar', 3, 6, 1
UNION SELECT 'Coffee', 8, 2, 2
UNION SELECT 'Meat', 7, 0, 12
UNION SELECT 'Milk', 16, 12, 2


DECLARE @item_count INT
SELECT @item_count = COUNT(*)
FROM @items


-- From: http://stackoverflow.com/questions/9507635/pivot-integer-bitwise-values-in-sql/9509598#9509598
DECLARE @bits TABLE
(
    number INT,
    [bit] INT,
    value INT
)


; with AllTheNumbers as (
    select cast (POWER(2, @item_count) as int) - 1 Number
    union all
    select Number - 1
    from AllTheNumbers
    where Number > 0
),
Bits as (
    select @item_count - 1 Bit
    union all
    select  Bit - 1
    from Bits
    where Bit > 0
)
INSERT INTO @bits (number, [bit], value)
select *, case when (Number & cast (POWER(2, Bit) as int)) != 0 then 1 else 0 end
from AllTheNumbers cross join Bits
order by Number, [Bit] desc


-- Table to hold trials - brute force!
DECLARE @trials TABLE
(
    trial_id INT,
    item_id INT,
    item_quantity INT
)


DECLARE @trial_max INT
SET @trial_max = (@protein_limit + @carb_limit + @fat_limit) * (POWER(2, @item_count))

DECLARE @trial_id INT
SET @trial_id = 1

DECLARE @base_quantity INT

WHILE @trial_id <= @trial_max
BEGIN

    SET @base_quantity = FLOOR((@trial_id / POWER(2, @item_count)))

    INSERT INTO @trials (trial_id, item_id, item_quantity)
    SELECT @trial_id + 1 + b.number
        , id
        , @base_quantity + b.value
    FROM @items a
    JOIN @bits b
        ON a.id = b.[bit] + 1



    --UPDATE @trials
    --SET item_quantity = @base_quantity + (@trial_id % item_id)
    --WHERE trial_id = @trial_id

    SET @trial_id = @trial_id + POWER(2, @item_count)

END


-- Get results of each trial
SELECT *
FROM @trials a
JOIN @items b
    ON a.item_id = b.id
ORDER BY a.trial_id

-- Use the trial_id field to reference the results of the previous select
SELECT *
FROM
(
    SELECT trial_id
        , SUM(protein * item_quantity) AS protein_total
        , SUM(carb * item_quantity) AS carb_total
        , SUM(fat * item_quantity) AS fat_total
    FROM @trials a
    JOIN @items b
        ON a.item_id = b.id
    GROUP BY trial_id
) a
WHERE protein_total <= @protein_limit
    AND carb_total <= @carb_limit
    AND fat_total <= @fat_limit
ORDER BY ((@protein_limit - protein_total) + (@carb_limit - carb_total) - (@fat_limit - fat_total)) ASC


-- This last query gets the best fit
SELECT c.name
    , b.item_quantity
FROM
(
    SELECT *
        , ROW_NUMBER() OVER (ORDER BY ((@protein_limit - protein_total) + (@carb_limit - carb_total) - (@fat_limit - fat_total)) ASC) AS rn
    FROM
    (
        SELECT trial_id
            , SUM(protein * item_quantity) AS protein_total
            , SUM(carb * item_quantity) AS carb_total
            , SUM(fat * item_quantity) AS fat_total
        FROM @trials a
        JOIN @items b
            ON a.item_id = b.id
        GROUP BY trial_id
    ) a
    WHERE protein_total <= @protein_limit
        AND carb_total <= @carb_limit
        AND fat_total <= @fat_limit
) a
JOIN @trials b
    ON a.trial_id = b.trial_id
JOIN @items c
    ON b.item_id = c.id
WHERE a.rn = 1

这将返回三个结果,每个结果都以不同的方式查看数据。

让我知道它是否有效!

答案 2 :(得分:0)

问题本身有点缺陷。您是否试图尽可能接近这些目标而不必过关?您是否只是在寻找与这些数字接近的“某种解决方案”?根据您如何定义哪些资格作为可接受的答案,这可能是一个非常容易或非常非常难以解决的问题。

例如,添加含有1g每种蛋白质,碳水化合物和脂肪的新成分。还要添加3种成分,每种成分含有1g独特的营养成分 - 一种是1g蛋白质,0种碳水化合物/脂肪,一种是1g碳水化合物0g蛋白质/脂肪等。这里你至少有两种,如果不是很多的解决方案,两者都会完全匹配目标。

让我们继续,并假设蛋白质食物对你很重要,所以你宁愿多吃1g / 1g / 1g营养素。如果我们不能完全达到目标,但是你没有喝15杯牛奶,那我们如何权衡解决方案。

背包问题是一个很好的开始,但有一百万种不同的方法可以解决这个问题,如果你打算尝试编写一个解决方案,我建议你尝试解决一些特定问题,然后尝试将其扩展为你了解幕后发生了什么。