从IEnumerable< T>中获取n个项目的最佳方法是什么?按随机顺序?
我正在编写商店API,需要从有时候大量枚举的项目中提供一小组随机项。底层的可枚举有时是一个数组,有时是所述数组的惰性求值过滤器。
由于我只是从枚举中抓取一定数量的项目,最好每次使用某种重复随机索引进行枚举和重复检查,而不是使用现有算法随机排序整个列表。抓住顶部x,对吧?
有更好的想法吗?
答案 0 :(得分:1)
这是另一个想法:
using System;
using System.Collections.Generic;
using System.Linq;
namespace RandomElements
{
class Program
{
static IEnumerable<int> GetRandomElements(IEnumerable<int> source, int count)
{
var random = new Random();
var length = source.Count();
var enumerator = source.GetEnumerator();
if (length < count)
{
throw new InvalidOperationException("Seriously?");
}
while (count > 0)
{
const int bias = 5;
var next = random.Next((length / bias) - count - bias) + 1; // To make sure we don't starve.
length -= next;
while (next > 0)
{
if (!enumerator.MoveNext())
{
throw new InvalidOperationException("What, we starved out?");
}
--next;
}
yield return enumerator.Current;
--count;
}
}
static void Main(string[] args)
{
var sequence = Enumerable.Range(1, 100);
var random = GetRandomElements(sequence, 10);
random.ToList().ForEach(Console.WriteLine);
}
}
}
它只需要经历一次枚举(如果传入ICollection,也就是说,它需要知道长度)。如果遍历枚举或复制所有元素或其他任何元素都很昂贵,这可能很有用。
我不是统计学家,数学家或魔术师,所以不要反对我,但我发现如果没有第22行引入的“偏见”,我觉得有点想从后端挑选更多序列。也许有人可以更多地调整概率?如果枚举真的很昂贵,你可以让它更偏向前方。
欢迎提出意见。
答案 1 :(得分:0)
在另一个答案中,我提供了一种从序列中返回a single random element的方法,只使用一次传递。
我怀疑这可以合理地调整,以便使用循环缓冲区并选择给定大小的随机序列,但是你必须非常小心才能使概率平衡。
答案 2 :(得分:0)
如果您使用Knuthe Shuffle,则可以在列表的一部分上进行随机随机播放。因此,没有必要对整个列表进行排序以获得n个随机项。我不知道这是否可以在您的约束中有效地完成,因为您仍然需要在应用算法之前将您正在抓取的内容转换为列表。
本质上,策略是抓取一个随机项,将其与列表的第一项交换。下次需要随机元素时,请跳过第一个。
答案 3 :(得分:0)
如果您事先知道项目数,那么计算该范围内的n个随机数是非常微不足道的,然后抓住那些带有这些索引的数据。