下面是一個應該可以工作的蠻力解決方案。這是一個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
這將返回三個結果,每個結果都以不同的方式查看數據。
讓我知道它是否有效!