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

时间:2011-08-31 14:51:30

标签: c#

在Python中,我可以这样做:

>>> import random
>>> ints = [1,2,3]
>>> random.choice(ints)
3

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

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

但是这需要索引,ints的重复也困扰着我。所以,我想出了这个:

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

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

4 个答案:

答案 0 :(得分:17)

以下是一些扩展方法:

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);

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

答案 1 :(得分:4)

排序效率会低得多。只需使用Skip(n)和First():

var randgen = new Random();
var ints = new int[] { 1, 2, 3};

ints.Skip(x =&gt; randgen.Next(0,ints.Count()))。First();

ints.ElementAt(x=> randgen.Next(0, ints.Count()));

答案 2 :(得分:3)

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

编辑:这里的巨大点......

如果您只想从列表中随机选择一个值...那么就这样做:

var myRandomValue = ints[(new Random()).Next(0, ints.Length)];

这是O(1)操作。

答案 3 :(得分:1)

如何简单易读:

ints[randgen.Next(ints.Length)];

说真的,为什么要用lambdas .OrderBy和.First和.Skip等来混淆代码!?