繁体   English   中英

如何在linq中优化FirstOrDefault语句

[英]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) -小于或等于ci元素的最大和。 N是元素数量(列表大小)。 W最大和(您的目标)。 对于每个c D(0,c) = 0 ,因为您没有元素:)然后将c1更改为W ,将i1更改为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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM