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等混淆你的代码!?