选择最低成本的组合
Posted
技术标签:
【中文标题】选择最低成本的组合【英文标题】:Selecting A combination of minimum cost 【发布时间】:2014-02-21 00:45:26 【问题描述】:我有不同餐厅不同项目的数据
Rest Item Price
----------------------
ABC dosa 14
ABC idly 30
ABC idly+upma 25
123 dosa 30
123 idly 7
123 upma 12
XYZ dosa 20
XYZ idly 12
XYZ upma 20
XYZ dosa+upma 30
XYZ dosa+idly+upma 40
Now I need to pickup a restaurant which gives me the best deal of "dosa+idly+upma" items.
从上面的例子:它将是餐厅“ABC”
我无法设计有效的方法来做到这一点,或者不知道该怎么做?有什么想法吗?
更新
这是我的对象的样子
Class Rest
Map<String,Integer> menu; //item,price map
【问题讨论】:
dosa+idly+upma ABC 价格为 39 卢比 ?? 给我们一些限制。输入的大小是多少?您希望搜索多少个项目?有多少项目?有多少家餐厅? @amit,这是样本数据。预计它会节省时间。数据没有太多限制。 @Reddy 那么规模是多少?许多?数百?数千?百万?如果它只有几十个 - 不要费心优化它。如果是数百万 - 那么,情况就完全不同了。 您的意思是您需要所有 3 件商品的最优最高价格,还是一件商品的价格比另一件更重要(例如,便宜的 dosa 比便宜的 upma 更重要)?我也饿了。 【参考方案1】:一种可能的贪婪算法的草图是:
-
遍历所有一元报价(例如 dosa、idly 或 upma)以找到每个报价的最小值。
遍历所有 binaray(例如 idly+upma)/三级 (...) 报价,比较它是否比一元报价便宜,如果是则替换。
您仍然需要对报价进行编码,但应该不会那么难。该算法会找到好的但不是必需的最佳解决方案,并且可能适用于非常小的样本。
实际上,您的问题与 背包或 TSP 问题相比,它们是 NP 完全的,因此只能在指数时间内解决。如果您想要一个解决方案,请考虑阅读大量论文并进行更多编码。这就是计算机科学的圣杯。 ;-)
更新:根据 TO 的要求,这里有一些指数伪代码草图:
foreach restaurant
create a list of all possible combinations of the offers // here's the exp!
filter those combinations that hold more/less than 1 dosy/idly/umpa
select minimum of the remaining ones
评论:这真的很丑,yuk! :-(
【讨论】:
必须有一个更有效的解决方案,然后对每个查询强制使用整个数据库... @LastFreeNickname,因为我不关注大规模数据,而且我知道它是长数据的 NP 问题,或者它不会处理大数据。对于这个简单的数据,假设我的餐厅不超过 5 家,每家餐厅的商品不超过 10 家。 @Reddy 那么你唯一的可能就是按照上面的解释强行使用它,并希望你的数据永远不会变大。 :-) 你可能会考虑一种比我更脏的方法来保持你的算法简单。【参考方案2】:计算可能的价格组合:遍历地图并解析字符串。存储每个价格组合。
过滤掉更贵的价格。
比较每家餐厅的剩余价格,返回价格最低的餐厅。
您还可以进行一些检查以最小化迭代,例如:
如果 idly 大于 idly+umpa,则不计算任何涉及 idly 的组合。【讨论】:
第一次也是这么想的,但是一旦您在这种方法中包含 >= 1 个商品,比较的顺序就会改变结果。说:idly+upma 可能会阻止 dosa+idly 替换,反之亦然。请参阅我的帖子以获取更多说明。 @LastFreeNickname 我不确定我是否理解你。您指的是第 2 步吗? 如果我理解线程开启器,任务也是用min_upmaidly替换min_upma和min_idly。这部分它丢失了。【参考方案3】:首先,您需要将 3 个项目的所有可能组合与不同的餐厅相对应,例如:
XYZ dosa+idly+upma 52
XYZ dosa+upma+idly 42
XYZ dosa+idly+upma 40
所有餐厅的情况都一样。
然后对价格进行排序,让最低价格重新获胜。
【讨论】:
假设 3 只是一个示例,您可能在一家餐厅有更多商品,而您只会要求其中的一部分 - 这种方法需要为每个餐厅创建2^n
不同的集合,其中 @如果您想在预处理中进行,987654323@ 是元素的总数。
@amit 根据要求,我已经提供了答案。
要求没有说 3 项。这个例子做了。
我怎么知道它的例子或要求?如果您想缩放项目,那么您也需要使用相同的逻辑。【参考方案4】:
这个问题是NP-Hard。我将展示从Set Cover Problem 减少的内容。
设置掩护问题 (SCP):
给定一组元素U
(在您的示例中为U=dosa,idly,upma
)和一组U
的子集,让它成为S
(例如S=dosa, idly,upma, upma
)找到最少数量的@ 子集987654328@ 使得它们的并集等于U
。
减少:
给定U
和S
的集合封面问题,创建一个关于一家餐厅的问题实例,使得S
中每个项目的价格(这是一个集合一项或多项)为 1。
现在,给定您问题的最佳解决方案 - 可能的最低价格,基本上是覆盖“宇宙”所需的最小子集数量。 给定集合覆盖问题的最优解 - 所需集合的数量是子集的最低价格。
结论: 由于我们已经看到有效地解决这个问题将有效地解决 SCP,我们可以得出结论,该问题是 NP-Hard,因此没有已知的多项式解决方案(并且大多数人认为不存在)。
替代方案是使用启发式解决方案或蛮力解决方案(只需搜索所有可能性,在指数时间内)。
【讨论】:
我就是这么说的。 :-) 虽然我将它与同级别的背包和 TSP 进行了比较。 @LastFreeNickname 是的,我看到你现在做到了。但我添加了一个正式的证明——我认为它有附加价值,你不是说吗?【参考方案5】:试试
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Mult
/**
* @param args
*/
public static void main(String[] args)
Map<String,List<X>> xx = new HashMap<String,List<X>>();
xx.put("ABC",new ArrayList<X>());
xx.get("ABC").add(new X("", 0));
xx.get("ABC").add(new X("dosa", 14));
xx.get("ABC").add(new X("idly", 30));
xx.get("ABC").add(new X("idly+upma", 25));
xx.put("123",new ArrayList<X>());
xx.get("123").add(new X("", 0));
xx.get("123").add(new X("dosa", 30));
xx.get("123").add(new X("idly", 7));
xx.get("123").add(new X("upma", 12));
xx.put("XYZ",new ArrayList<X>());
xx.get("XYZ").add(new X("", 0));
xx.get("XYZ").add(new X("dosa", 20));
xx.get("XYZ").add(new X("idly", 12));
xx.get("XYZ").add(new X("upma", 20));
xx.get("XYZ").add(new X("dosa+upma", 30));
xx.get("XYZ").add(new X("dosa+idly+upma", 40));
String[] t =
"dosaidlyupma",
"idlydosaupma",
"upmaidlydosa",
"dosaupmaidly",
"upmadosaidly",
"idlyupmadosa";
Set<String> targets = new HashSet<String>(Arrays.asList(t));
Map<String,Integer> best = new HashMap<String,Integer>();
for(String restaurant:xx.keySet())
best.put(restaurant, Integer.MAX_VALUE);
String combo = null;
for(X a:xx.get(restaurant))
int deal = a.price;
combo = a.item;
for(X b:xx.get(restaurant))
deal = deal + b.price;
combo = combo + "+" + b.item;
for(X c:xx.get(restaurant))
deal = deal + c.price;
combo = combo + "+" + c.item;
if (targets.contains(combo.replaceAll("\\+", "")))
// System.out.println(restaurant+"\t"+combo.replaceAll("\\+", "")+"\t"+deal);
if (best.get(restaurant) > deal)
best.put(restaurant, deal);
System.out.println(best);
会给你
XYZ=40, ABC=39, 123=49
这是旧的蛮力方法。
不是最好的,但对于这个小套装,它可以工作。
【讨论】:
以上是关于选择最低成本的组合的主要内容,如果未能解决你的问题,请参考以下文章