Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# LINQ表达式优化_C#_Linq - Fatal编程技术网

C# LINQ表达式优化

C# LINQ表达式优化,c#,linq,C#,Linq,我有下面的代码,我的目标是得到10个整数列表的随机结果集,其中每个列表包含4个整数,它们的相加超过80 var numList = new List<int> { 5, 20, 1, 7, 19, 3, 15, 60, 3, 21, 57, 9 }; var extractedList = (from n1 in numList from n2 in numList

我有下面的代码,我的目标是得到10个整数列表的随机结果集,其中每个列表包含4个整数,它们的相加超过80

        var numList = new List<int> { 5, 20, 1, 7, 19, 3, 15, 60, 3, 21, 57, 9 };

        var extractedList = (from n1 in numList
                             from n2 in numList
                             from n3 in numList
                             from n4 in numList
                             where n1 + n2 + n3 + n4 > 80
                             select new { n1, n2, n3, n4 }).ToList();
var numList=新列表{5,20,1,7,19,3,15,60,3,21,57,9};
var extractedList=(从numList中的n1开始)
从numList中的n2开始
从numList中的n3开始
从numList中的n4开始
其中n1+n2+n3+n4>80
选择新的{n1,n2,n3,n4});

现在我知道我可以很容易地将“.GetRange(startIndex,10)”添加到“.ToList()”中,但在这种情况下,提取的列表将有8799个项目,添加“GetRange”是否意味着只有10个项目加载到内存中,或者是否意味着先加载8799个项目,然后过滤到10个项目,我是LINQ的新手,所以我希望有一种有效的方法来实现这一点,因为我知道任何数字都不会超过200,使用8位字节有助于提高性能,任何关于这方面的建议都会很好。

使用
采取
方法只加载特定数量的项目,而不是全部项目,然后调用
ToList

(from n1 in numList
 from n2 in numList
 from n3 in numList
 from n4 in numList
 where n1 + n2 + n3 + n4 > 80
 select new { n1, n2, n3, n4 }).Take(10).ToList();
正如注释中已经指出的,这将始终给出相同的结果。您可以随机化列表,然后执行查询(而不是对数千个组合进行排序)


如果您只想要前10项,您可以使用:

var numList=新列表{5,20,1,7,19,3,15,60,3,21,57,9}

    var extractedList = (from n1 in numList
                         from n2 in numList
                         from n3 in numList
                         from n4 in numList
                         where n1 + n2 + n3 + n4 > 80
                         select new { n1, n2, n3, n4 }).Take(10).ToList();

但是,它将始终产生相同的10项。

如果您想继续使用LINQ:

var numList = new List<int> { 5, 20, 1, 7, 19, 3, 15, 60, 3, 21, 57, 9 };

Random rnd = new Random();
var extractedList = (from n1 in numList
                     from n2 in numList
                     from n3 in numList
                     from n4 in numList
                     where n1 + n2 + n3 + n4 > 80
                     select new
                     {
                         n1,
                         n2,
                         n3,
                         n4,
                         Rnd = rnd.NextDouble()
                     })
                     .OrderBy(z => z.Rnd)
                     .Take(10)
                     .ToList();
var numList=新列表{5,20,1,7,19,3,15,60,3,21,57,9};
随机rnd=新随机();
var extractedList=(从numList中的n1开始)
从numList中的n2开始
从numList中的n3开始
从numList中的n4开始
其中n1+n2+n3+n4>80
选择新的
{
n1,
n2,
n3,
n4,
Rnd=Rnd.NextDouble()
})
.OrderBy(z=>z.Rnd)
.Take(10)
.ToList();

请注意,我添加了一个随机参数,所以您不会总是得到相同的结果。

如果您在列表中调用.GetRange,您必须首先拥有一个列表。 因此,整个查询将被枚举,结果将在一个列表中,然后从该列表中获取10项

如果您只需要这10个项目,就可以使用Skip and Take来处理查询本身

var extractedList = (from n1 in numList
                     from n2 in numList
                     from n3 in numList
                     from n4 in numList
                     where n1 + n2 + n3 + n4 > 80
                     select new { n1, n2, n3, n4 })
                     .Skip(startIndex).Take(10);

但是,如果要将结果拆分为长度为10的部分,则每次都会枚举整个查询,因此您不希望这样。在这种情况下,将整个结果存储在列表中(就像您在示例中所做的那样)会更好。

使用
Take
LINQ表达式

var numList = new List<int> { 5, 20, 1, 7, 19, 3, 15, 60, 3, 21, 57, 9 };

    var extractedList = (from n1 in numList
                         from n2 in numList
                         from n3 in numList
                         from n4 in numList
                         where n1 + n2 + n3 + n4 > 80
                         select new { n1, n2, n3, n4 }).Take(10).ToList();
var numList=新列表{5,20,1,7,19,3,15,60,3,21,57,9};
var extractedList=(从numList中的n1开始)
从numList中的n2开始
从numList中的n3开始
从numList中的n4开始
其中n1+n2+n3+n4>80
选择新的{n1,n2,n3,n4});
这将只取通过您选择标准的前10个结果。要从随机化集合中获得该选择,OrderBy(x=>rnd.Next())将有所帮助,但这将对整个集合进行排序,因此这取决于您是否可以随机化集合而不影响性能(例如在后台)

或者,您可以考虑编写自己的ILIST扩展,如:

IList<T> GetRandomElements(this IList<T> me, int numElements)
{
    var copyOfMe = new List<T>(me);
    List<T> results = new List<T>();
    Random rnd = new Random();
    for(int i=0; i<numElements;i++)
    {
        if(copyOfMe.Count > 0)
        {
            int index = Random.Next(0,results.Count);
            results.Add(copyOfMe[index]);
            copyOfMe.Remove(index);
        }
    }
}
IList GetRandomElements(此IList me,整数元素)
{
var copyOfMe=新列表(me);
列表结果=新列表();
随机rnd=新随机();
对于(int i=0;i 0)
{
int index=Random.Next(0,results.Count);
结果。添加(copyOfMe[索引]);
删除(索引)的副本;
}
}
}

但是这确实需要一个IList输入(用于索引)。

这里发布的其他解决方案的问题在于,它们是O(n4),因为它们在进行过滤之前会从列表中生成四个项目的所有可能组合。对于问题中给出的短名单来说,这可能很好,但不会扩大规模

例如,定义
var numList=Enumerable.Range(10,50).ToList()(因此列表中有50项)导致此处的其他解决方案需要大约15秒;下面更高效的版本只需几分之一秒

诀窍是定义一个生成器函数,Linq只调用完成查询所需的最小次数(尽管
while(true)
使其看起来像一个无限循环,但
yield return
使其每次返回一个值):


ToList
将创建一个包含所有项目的列表,然后您将从中提取十个项目并从中创建一个列表。请注意,像
21,21,21,21
20,21,20,21
19,20,21,57
57,22,20,19
int
这样的重复通常是在中使用的最有效的类型。请注意,这将始终产生前10项;所以numList应该随机化。此外,它还将优先于列表前面的项目。OrderBy子句是否会枚举整个集合以对其进行排序?
IList<T> GetRandomElements(this IList<T> me, int numElements)
{
    var copyOfMe = new List<T>(me);
    List<T> results = new List<T>();
    Random rnd = new Random();
    for(int i=0; i<numElements;i++)
    {
        if(copyOfMe.Count > 0)
        {
            int index = Random.Next(0,results.Count);
            results.Add(copyOfMe[index]);
            copyOfMe.Remove(index);
        }
    }
}
static IEnumerable<Tuple<int, int, int, int>> Generate(IList<int> list)
{
    int max = list.Count;
    Random rnd = new Random();
    while (true)
    {
        yield return new Tuple<int, int, int, int>(
            rnd.Next(max), rnd.Next(max), rnd.Next(max), rnd.Next(max));
    }
}
var quickList = Generate(numList)
    .Where(t => t.Item1 + t.Item2 + t.Item3 + t.Item4 > 80)
    .Distinct().Take(10).ToList();