我有两个linq语句,第一个占用25毫秒,第二个占用1100秒,循环100,000。
我已经用ElementAt替换了FirstAll,甚至使用foreach来获取第一个元素,但仍然需要相同的时间。 有没有更快的方法来获得第一个元素?
我考虑过其他一些问题,但仍无法找到解决此问题的任何解决方案。
var matches = (from subset in MyExtensions.SubSetsOf(List1)
where subset.Sum() <= target
select subset).OrderByDescending(i => i.Sum());
var match = matches.FirstOrDefault(0);
也尝试过:
foreach (var match in matches)
{
break;
}
甚至:
var match = matches.ElementAt(0);
任何意见都将不胜感激。
编辑:这是SubSetOf的代码
public static class MyExtensions
{
public static IEnumerable<IEnumerable<T>> SubSetsOf<T>(this IEnumerable<T> source)
{
// Deal with the case of an empty source (simply return an enumerable containing a single, empty enumerable)
if (!source.Any())
return Enumerable.Repeat(Enumerable.Empty<T>(), 1);
// Grab the first element off of the list
var element = source.Take(1);
// Recurse, to get all subsets of the source, ignoring the first item
var haveNots = SubSetsOf(source.Skip(1));
// Get all those subsets and add the element we removed to them
var haves = haveNots.Select(set => element.Concat(set));
// Finally combine the subsets that didn't include the first item, with those that did.
return haves.Concat(haveNots);
}
}
答案 0 :(得分:5)
你两次打电话给Sum--这很糟糕。预先知道:
var matches = MyExtensions.SubSetsOf(List1)
.Select(subset => new { subset, Sum = subset.Sum() })
.Where(o => o.Sum < target).OrderByDescending(i => i.Sum);
var match = matches.FirstOrDefault();
var subset = match != null ? match.subset : null;
答案 1 :(得分:3)
正如Jason所说,它是subset sum problem - Knapsack Problem的选项,其中weight
等于value
。最简单的解决方案 - 生成所有子集并检查它们的总和,但这种算法具有可怕的复杂性。所以,我们的优化并不重要。
你应该使用动态programmig来解决这个问题:
假设二维数组D(i, c)
- i
元素的最大总和小于或等于c
。 N
- 是元素的数量(列表大小)。 W
- 最大总和(您的目标)。
每个D(0,c) = 0
c
,因为你没有元素:)
将c
从1
更改为W
并将i
从1
更改为N
让我们计算
D(i,c) = max(D(i-1,c),D(i-1,c-list[i])+list[i])
。
要恢复子集,我们必须存储父节点数组并在计算期间设置它们。 另一个例子是here。 整个代码:
class Program
{
static void Main(string[] args)
{
var list = new[] { 11, 2, 4, 6 };
var target = 13;
var n = list.Length;
var result = KnapSack(target, list, n);
foreach (var item in result)
{
Console.Write(item + " ");
}
}
private static List<int> KnapSack(int target, int[] val, int n)
{
var d = new int[n + 1, target + 1];
var p = new int[n + 1, target + 1];
for (var i = 0; i <= n; i++)
{
for (var c = 0; c <= target; c++)
{
p[i, c] = -1;
}
}
for (int i = 0; i <= n; i++)
{
for (int c = 0; c <= target; c++)
{
if (i == 0 || c == 0)
{
d[i, c] = 0;
}
else
{
var a = d[i - 1, c];
if (val[i - 1] <= c)
{
var b = val[i - 1] + d[i - 1, c - val[i - 1]];
if (a > b)
{
d[i, c] = a;
p[i, c] = p[i - 1, c];
}
else
{
d[i, c] = b;
p[i, c] = i - 1;
}
}
else
{
d[i, c] = a;
p[i, c] = p[i - 1, c];
}
}
}
}
//sum
//Console.WriteLine(d[n, target);
//restore set
var resultSet = new List<int>();
var m = n;
var s = d[n, target];
var t = p[m, s];
while (t != -1)
{
var item = val[t];
resultSet.Add(item);
m--;
s -= item;
t = p[m, s];
}
return resultSet;
}
}
答案 2 :(得分:2)
看起来您正在尝试解决的一般问题是找到最大总和小于target
的数字子集。 linq函数的执行时间是解决方案的症状。这是一个众所周知且研究得很多的问题,称为“knapsack problem”。我相信你的特定变体将属于“有界背包问题”类,其重量等于该值。我将从研究开始。您实施的解决方案,强制每个可能的子集,被称为“天真”解决方案。我很确定它是所有可能解决方案中表现最差的。