获得随机子集合的最佳LINQ查询 - Shuffle

Job*_*Joy 44 c# linq ienumerable observablecollection

请建议一种最简单的方法,从具有'N'项的集合中获取计数'n'的随机混洗集合.其中n <= N

Luk*_*keH 102

继mquander的回答和Dan Blanchard的评论之后,这里是一个LINQ友好的扩展方法,执行Fisher-Yates-Durstenfeld的随机播放:

// take n random items from yourCollection
var randomItems = yourCollection.Shuffle().Take(n);

// ...

public static class EnumerableExtensions
{
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
    {
        return source.Shuffle(new Random());
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (rng == null) throw new ArgumentNullException("rng");

        return source.ShuffleIterator(rng);
    }

    private static IEnumerable<T> ShuffleIterator<T>(
        this IEnumerable<T> source, Random rng)
    {
        var buffer = source.ToList();
        for (int i = 0; i < buffer.Count; i++)
        {
            int j = rng.Next(i, buffer.Count);
            yield return buffer[j];

            buffer[j] = buffer[i];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Htbaa:该方法返回一个延迟评估的序列.如果你多次执行`seq.Shuffle().ElementAt(n)`那么你每次都要重新洗牌,这样你很可能会在'n`位置得到一个不同的项目.如果你想要将*重复*一次*那么你需要将序列存储在某种具体的集合中:例如,`var list = seq.Shuffle().ToList()`.然后你可以随心所欲地执行`list.ElementAt(n)` - 或者只是`list [n]` - 你将总是得到相同的项目. (3认同)
  • @jocull如果您使用的版本不采用Random实例,它将为您创建一个.随时间创建的随机实例具有使用相同种子的风险,因此给出相同的随机数序列.如果在不同的线程上将相同的Random实例传递给它,则会得到不可预测的结果,因为Random的实例不是线程安全的.您应该使用不同的种子创建Random实例,而不是在线程之间共享它们. (2认同)

Sco*_*ell 37

另一种选择是使用OrderBy并对GUID值进行排序,您可以使用以下命令对其进行排序:

var result = sequence.OrderBy(elem => Guid.NewGuid());
Run Code Online (Sandbox Code Playgroud)

我做了一些实证测试来说服自己,上面实际上产生了一个随机分布(它似乎做了).您可以在随机重新排序数组的技术中查看我的结果.

  • 此解决方案违反了orderby的约定,特别是给定对象在整个排序中具有一致的键.如果它确实有效,它只能通过机会来实现,并且可能在框架的未来版本中出现问题.有关详细信息,请参阅http://blogs.msdn.com/b/ericlippert/archive/2011/01/31/spot-the-defect-bad-comparisons-part-four.aspx (17认同)
  • 这里的问题不是密钥不一致.约翰梅尔维尔误读了我的文章; 该文章指出,进行*比较* - 也就是说,此项目更大,更小或等于另一个 - 这是不一致的,违反了Sort方法的合同.这个答案是完全不同的错误:**Guids只保证是唯一的; 他们的随机性是一个你不应该依赖的实现细节**. (13认同)
  • 特别是,从初始随机元素*顺序*生成guid是合法的; 这仍然是对独特性的良好保证.仅仅因为guid生成器*今天*实际上并不生成顺序guid,实现细节可能会发生变化,如果它确实发生了变化,那么每次突然你的"shuffle"将事情"拖曳"为排序顺序.使用guids生成*唯一性*,从不*随机*.使用旨在为随机性生成随机性的类. (7认同)
  • 我只是偶然发现了这个页面并认为它很棒,但我不明白这个评论.使用这种方法来收集一个集合可能会出错(我不是在讽刺地问,我真的很好奇这些可能性)? (5认同)

小智 13

这有一些"随机偏见"的问题,我相信它不是最优的,这是另一种可能性:

var r = new Random();
l.OrderBy(x => r.NextDouble()).Take(n);
Run Code Online (Sandbox Code Playgroud)


mqp*_*mqp 6

洗牌收集到一个随机顺序取前n从结果项.

  • 请注意,当n <N时,您不必完全改变集合.您只需要遍历Durstenfeld的算法n次,并获取(部分)洗牌结果的最后n项. (6认同)