我有时使用OrderBy (item => R.NextDouble())
对列表或数组进行随机播放,其中Random R
在其他位置初始化。
现在这显然是一个黑客,虽然在这个网站上到处推荐的那个,并且它在实践中确实很好用。
然而,还有人指出,这使得假设排序算法不会因改变单个项目的值而变得混淆。进入一个无限循环。
我的问题是,是否存在某种隐含或明确的保证,这种情况不会发生。我在MSDN中找不到任何关于它的信息。
虽然有非常好的,专门的O(n)算法用于此目的,但我并不总是想在小方项目中搜索和复制它们(我们不是在讨论prod)。他们显然是正确的解决方案。
请注意,性能根本不是问题。此外,不需要良好或安全的随机性(在这种情况下必须使用加密库) - 这只是稍微改组一些东西。
答案 0 :(得分:4)
根据文档,这不能保证有效。
我偷看了内部。排序算法首先将所有排序键捕获到临时数组中,然后再从不调用它们。所以在当前的实现中这是安全的。新的.NET版本甚至补丁都可以使这种技术无效。
你可能会争辩说,出于兼容性原因,这种情况永远不会改变,而真正可能的情况就像95%(来源:我)。微软有很高的compat标准。
另一个问题是,如果为两个键生成相同的值,它们将不会随机定位。相反,它们将基于排序算法的内部定位,这可能是确定性的。例如,他们可能永远不会切换他们的相对顺序。
我只会依赖短期投掷项目。
这种精神的另一个黑客是按Guid.NewGuid()
排序。这为您节省了另一行代码并且没有播种问题。另一方面,guid可能有一个模式,甚至可能是顺序的。也很成问题。
在生产代码的代码审查中,我会失败。
答案 1 :(得分:2)
不能保证继续正常工作。
它不太可能永远停止工作,因为这种改变需要改变关键值的预先计算,这在许多情况下会非常昂贵。因此,打破它的变化不太可能发生。从调用选择器的频率来看,它也是其他方式的可观察变化。这种可观察到的变化并没有被排除(我已经使用了.NET的PR,减少了接受其他linq方法调用选择器的频率)但是这种变化必须具有非常强大的好处,特别是因为它会增加而不是减少通话次数。
它当前不能很好地工作,因为Enumerable.OrderBy
(与linq中的其他OrderBy
方法不同,例如Queryable
或PLinq中的方法)保证提供稳定的排序(等值始终按原始顺序排列)会给你一个偏见。
对于快速编写随机排序非常有用,其中随机播放的质量只需要非常好。对于更强大的随机播放,请使用以下内容:
public static class Shuffler
{
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, int seed)
{
return new ShuffledEnumerable<T>(source, seed);
}
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
return new ShuffledEnumerable<T>(source);
}
private class ShuffledEnumerable<T> : IEnumerable<T>
{
private IEnumerable<T> _source;
private int? _seed;
public ShuffledEnumerable(IEnumerable<T> source)
{
_source = source;
}
public ShuffledEnumerable(IEnumerable<T> source, int seed)
: this(source)
{
_seed = seed;
}
public IEnumerator<T> GetEnumerator()
{
Random rnd = _seed.HasValue ? new Random(_seed.GetValueOrDefault()) : new Random();
T[] array = _source.ToArray();
int count = array.Length;
for (int i = array.Length - 1; i > 0; --i)
{
int j = rnd.Next(0, i + 1);
if (i != j)
{
T swapped = array[i];
array[i] = array[j];
array[j] = swapped;
}
}
return ((IEnumerable<T>)array).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
如果您需要通过将Random
替换为合适的加密PRNG来获得随机性的加密质量,则可以再次进一步改进。
答案 2 :(得分:2)
虽然它有效并且很可能不会改变,但这并不能保证。但如果您希望100%确定,您可以通过使用显式投影来保证自己
enumerable.Select(item => new { Source = item, Order = R.NextDouble() }.OrderBy(item => item.Order).Select(item.Source)
这当然更加冗长,但仍然很容易实现快速而肮脏的方法。