我有一个包含上述属性的项目列表。 ID 量 PackageNbr 我需要完成的是从这个列表中获得具有 NEAREST ORAL 金额总和的项目到特定值;但条件应始终有效,即返回的项应来自不同的PackageNbr。 例如。
LISTOFITEMS:
╔══════╦══════════╦═════════════╗
║ ID ║ amount ║ packageNbr ║
╠══════╬══════════╬═════════════╣
║ 1 ║ 10 ║ 1 ║
║ 2 ║ 7 ║ 1 ║
║ 3 ║ 4 ║ 1 ║
║ 4 ║ 6 ║ 2 ║
║ 5 ║ 5 ║ 2 ║
║ 6 ║ 3 ║ 2 ║
║ 7 ║ 10 ║ 3 ║
║ 8 ║ 9 ║ 3 ║
║ 9 ║ 3 ║ 4 ║
║ 10 ║ 2 ║ 5 ║
╚══════╩══════════╩═════════════╝
对于值21,returnedItems的id为:1 - 6 - 8
对于值14,returnedItems的id为:3 - 7
对于值11,returnedItems的id是:4 - 9 - 10
我认为通过获得所有总和组合(具有不同的PackageNbr)可以实现上述目标,之后我们可以得到最接近或相等的。
知道如何完成这项任务吗? 我在网上冲浪,并没有找到使用linq的方法。 任何帮助将不胜感激。
此致
答案 0 :(得分:2)
我认为你的问题映射到计算机科学中众所周知的问题Subset Sum Problem
如果您没有其他约束并且必须找到所有可能的组合,那么为了缩短它,您最终会得到一个指数算法。
现在找到一个实用的解决方案:
如果您有一个很少更改的数据集,并且您需要使用不同的总和多次查询它,只需构建一个包含相应总和的所有组合的表。按总和对其进行排序,并使用二进制搜索来获得具体总和的结果。
如果您的数据集几乎相同,但相对频繁地更改,您仍然可以创建一个包含所有组合和相应总和的排序数据结构。但是,您需要在原始数据集中每次添加或删除后保持最新。这比再次重新制作还要便宜。
如果你总是有一个新的数据集和新的查询值,你最终会得到wiki文章中描述的解决方案之一。
这是选项1的示例实现。 它使用combinatorics library的NUGet来生成组合。
//Dataset
var items = new[] {
new Item {Id=1, Amount=10, PackageNr=1},
new Item {Id=2, Amount=7, PackageNr=1},
new Item {Id=3, Amount=4, PackageNr=2},
new Item {Id=4, Amount=3, PackageNr=2},
new Item {Id=5, Amount=8, PackageNr=3},
new Item {Id=6, Amount=9, PackageNr=3},
new Item {Id=7, Amount=10, PackageNr=4},
};
//Building a table
var stack = new List<Tuple<int, int[]>>();
for(int count=1; count <= items.Count(); count++) {
stack.AddRange(
new Combinations<Item>(items, count)
.Where(combination => !combination
.GroupBy(item => item.PackageNr)
.Where(group => group.Count() > 1)
.Any())
.Select(combination => new Tuple<int, int[]>(
combination.Sum(item=>item.Amount),
combination.Select(item=>item.Id).ToArray())));
}
var table =
stack
.OrderBy(tuple => tuple.Item1)
.ToArray();
var index = table.Select(i => i.Item1).ToArray();
//Print the table
foreach (var row in table)
{
Console.WriteLine(" {0} -> {1}", row.Item1, string.Join(",", row.Item2));
};
//Binary search in the table.
Console.WriteLine("Number to search for:");
int number;
int.TryParse(Console.ReadLine(), out number);
var position = Array.BinarySearch(index, number);
if (position >= 0) Console.WriteLine("Exact match {0}", string.Join(",", table[position].Item2));
else Console.WriteLine("Best match {0}", string.Join(",", table[~position].Item2));
Console.ReadKey();
}