表达式优化
本文关键字:优化 表达式 | 更新日期: 2023-09-27 18:05:16
我有下面的代码,我的目标是获得随机结果集,其中有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();
现在我知道我可以很容易地添加"。GetRange (startIndex, 10)"到".ToList()",但在这个场景中提取的列表将有8799件,将添加"GetRange"意味着只有10项加载到内存,或者它将意味着8799年就被加载,然后过滤到10,我是一个LINQ新手所以我希望有一个有效的方法,也因为我知道没有数量将超过200,将使用一个8位字节帮助性能,任何建议这将是伟大的。
如果您只想要前10项,您可以使用:
var numList = new List {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项。
使用Take
方法只加载特定数量的项目而不是所有项目,然后调用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();
正如已经在注释中指出的那样,这将总是给出相同的结果。您可以随机化列表,然后执行查询(而不是对数千个组合进行排序)
Random rnd = new Random();
numList = numList.OrderBy(x => rnd.Next()).ToArray();
(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();
如果你想继续使用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();
请注意,我已经添加了一个随机参数,所以你不会总是得到相同的结果。
如果你在List上调用。getrange,你必须首先有一个List。因此,整个查询将被枚举,结果将在一个列表中,然后从该列表中取出10个项目。
如果你只想要这10个条目,你可以对查询本身使用Skip和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的部分,那么每次都会枚举整个查询,因此您不希望这样做。在这种情况下,将整个结果存储在List中(就像您在示例中所做的那样)会更好。
使用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();
它将只接受通过选择标准的前10个结果。要从随机集合中获得选择,OrderBy(x => rand . 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);
}
}
}
但这确实需要一个illist输入(用于索引)。
这里发布的其他解决方案的问题是,它们是O(n4),因为它们在进行过滤之前从列表中生成四个项目的所有可能组合。对于问题中给出的短列表,这可能没问题,但无法扩展。
例如,定义var numList = Enumerable.Range(10, 50).ToList();
(因此列表中有50个项目)会导致这里的其他解决方案花费大约15秒;下面这个更有效的版本只需要几分之一秒。
while (true)
使它看起来像一个无限循环,但yield return
使它一次返回一个值):
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));
}
}
然后你可以用Linq来调用它,像这样:
var quickList = Generate(numList)
.Where(t => t.Item1 + t.Item2 + t.Item3 + t.Item4 > 80)
.Distinct().Take(10).ToList();