有效提取大型可枚举的小型随机子集的最佳方法是什么?

时间:2009-03-30 14:03:47

标签: c# .net sorting random

从IEnumerable< T>中获取n个项目的最佳方法是什么?按随机顺序?

我正在编写商店API,需要从有时候大量枚举的项目中提供一小组随机项。底层的可枚举有时是一个数组,有时是所述数组的惰性求值过滤器。

由于我只是从枚举中抓取一定数量的项目,最好每次使用某种重复随机索引进行枚举和重复检查,而不是使用现有算法随机排序整个列表。抓住顶部x,对吧?

有更好的想法吗?

4 个答案:

答案 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个随机数是非常微不足道的,然后抓住那些带有这些索引的数据。