[英]how to optimize FirstOrDefault Statement in linq
我有两个linq语句,第一个语句花费25毫秒,第二个语句花费100,000毫秒的循环时间1100毫秒。
我已经用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);
}
}
您两次致电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;
正如Jason所说的,这是一个子集和问题 - weight
等于value
的背包问题 。 最简单的解决方案-生成所有子集并检查其和,但是此算法具有可怕的复杂性。 因此,我们的优化并不重要。
您应该使用动态programmig解决此问题:
假设一个二维数组D(i, c)
-小于或等于c
的i
元素的最大和。 N
是元素数量(列表大小)。 W
最大和(您的目标)。 对于每个c
D(0,c) = 0
,因为您没有元素:)然后将c
从1
更改为W
,将i
从1
更改为N
让我们计算D(i,c) = max(D(i-1,c),D(i-1,c-list[i])+list[i])
。
要恢复子集,我们必须存储父级数组并在计算期间进行设置。 这里还有另一个例子。 整个代码:
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;
}
}
看来您要解决的一般问题是找到总和小于target
的数字子集。 linq函数的执行时间是解决方案的症状。 这是一个众所周知的且经过大量研究的问题,称为“ 背包问题 ”。 我相信您的特定变体将属于权重等于值的“有限制的背包问题”类别。 我将从研究开始。 您已实施的解决方案,将所有可能的子集强制使用,称为“天真”解决方案。 我很确定这是所有可能解决方案中性能最差的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.