C#:从IEnumerable获取随机值的优雅代码

Geo*_*rge 17 c#

在Python中,我可以这样做:

>>> import random
>>> ints = [1,2,3]
>>> random.choice(ints)
3
Run Code Online (Sandbox Code Playgroud)

在C#中,我做的第一件事是:

var randgen = new Random();
var ints = new int[] { 1, 2, 3 };
ints[randgen.Next(ints.Length)];
Run Code Online (Sandbox Code Playgroud)

但这需要索引,也需要复制ints困扰我.所以,我想出了这个:

var randgen = new Random();
var ints = new int[] { 1, 2, 3 };
ints.OrderBy(x=> randgen.Next()).First();
Run Code Online (Sandbox Code Playgroud)

仍然不是很好,很有效率.是否有更优雅的方式从IEnumberable获取随机值?

Dom*_*nic 21

这里有几种扩展方法:

public static T RandomElement<T>(this IEnumerable<T> enumerable)
{
    return enumerable.RandomElementUsing<T>(new Random());
}

public static T RandomElementUsing<T>(this IEnumerable<T> enumerable, Random rand)
{
    int index = rand.Next(0, enumerable.Count());
    return enumerable.ElementAt(index);
}

// Usage:
var ints = new int[] { 1, 2, 3 };
int randomInt = ints.RandomElement();

// If you have a preexisting `Random` instance, rand, use it:
// this is important e.g. if you are in a loop, because otherwise you will create new
// `Random` instances every time around, with nearly the same seed every time.
int anotherRandomInt = ints.RandomElementUsing(rand);
Run Code Online (Sandbox Code Playgroud)

对于一般情况IEnumerable<T>,这将是O(n),因为这是.Count()随机.ElementAt()调用的复杂性; 但是,对于数组和列表都是特殊情况,因此在这些情况下它将是O(1).

  • 真正; Jon Skeet在http://stackoverflow.com/questions/648196/random-row-from-linq-to-sql/648240#648240上的答案可能是这方面最好的答案. (4认同)
  • 但请注意,它会对数据进行两次传递,如果数据集在调用"Count"和调用"ElementAt"之间发生变化,则会导致问题.(这里不是问题,但一般来说可能是一个问题.) (2认同)
  • `rand.Next`永远不会选择它的上限. (2认同)

Tim*_*uri 4

不,这基本上是最简单的方法。当然,这只是半随机的,但我认为它适合大多数需求。

编辑:这里很重要......

如果您只想从列表中随机选择一个值...那么只需执行以下操作:

var myRandomValue = ints[(new Random()).Next(0, ints.Length)];
Run Code Online (Sandbox Code Playgroud)

这是一个 O(1) 的操作。

  • random 类生成伪随机数,而不是加密的强随机数。 (3认同)