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

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

但这需要索引,而且重复的注意事项困扰着我。 所以,我想出了这个:

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

仍然不是非常好,高效。 是否有更优雅的方式从IEnumberable获取随机值?

这里有几种扩展方法:

 public static T RandomElement(this IEnumerable enumerable) { return enumerable.RandomElementUsing(new Random()); } public static T RandomElementUsing(this IEnumerable 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 eg 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 ,这将是O( n ),因为这是.Count()和随机.ElementAt()调用的复杂性; 但是,对于数组和列表都是特殊情况,因此在这些情况下它将是O(1)。

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

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

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

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

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

编辑: 巨大的点在这里……

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

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

这是O(1)操作。

简单易读的东西怎么样:

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

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