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

C#:从IEnumerable中获取随机值的优雅代码,c#,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的重复也让我感到困扰。所以,我想到了

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

仍然不是很好和有效。有没有更优雅的方法从IEnumerable中获取随机值?

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

编辑:这里有一个巨大的点…

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

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

这是一个O(1)操作。

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

跳过(x=>randgen.Next(0,ints.Count())).First()


简单易读的东西怎么样:

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

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

这里有几个扩展方法供您使用:

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);
public static T random元素(此IEnumerable可枚举)
{
返回enumerable.RandomElementUsing(new Random());
}
公共静态T RandomElementUsing(此IEnumerable可枚举,随机rand)
{
int index=rand.Next(0,enumerable.Count());
返回可枚举的.ElementAt(索引);
}
//用法:
var ints=新的int[]{1,2,3};
int randomInt=ints.RandomElement();
//如果您有一个预先存在的'Random'实例,请使用它:
//这很重要,例如,如果您处于循环中,因为否则您将创建新的
//“随机”实例,每次都有几乎相同的种子。
int-anotherRandomInt=int.RandomElementUsing(兰德);

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

假设您的意思是
IEnumerable
,但考虑到这个问题,我从
IEnumerable
:)中得到了一个LOL。您的
IEnumerable
的来源是什么?RandomElement函数似乎就是您要寻找的。@JonH:No,我不想生成随机枚举。什么是“半随机”?random类生成伪随机数,而不是加密强随机数。@Chris我知道什么是伪随机数。我不知道“半随机”是什么意思。而伪随机并不排除它的密码安全性,例如:
密码强!=(!pseudo-random)
这可能很容易,但代价很高,因为它会对整个枚举进行排序。有一些著名的随机选择单遍算法,它们对枚举进行递增操作。伪随机!=密码学上很强。如果你能猜出这个值,你就可以破解基于它的密码。From:“这个术语“加密性强”通常用于描述加密算法,与其他一些算法(因此加密性弱)相比,意味着更大的抗攻击性……在这种用法中,这个术语的意思是难以猜测。”最后一个paren应该在
.First()之前吗
?为什么要使用
.Skip(N).First()
而不是
.ElementAt(N)
?你完全正确。ElementAt(N)对于列表将更快。将进行相应的编辑。[]不适用于IEnumerable,这正是海报所要求的。我不认为海报需要基于数组或列表的解决方案,正如您所指出的,这很简单。但是请注意,它会对数据进行两次传递,如果在调用
Count
和调用
ElementAt
之间数据集发生更改,则可能会导致问题。(此处不是问题,但可能是一般问题)正确;Jon Skeet在的答案可能是这方面最好的答案。如果它查找ElementAt(enum.Count())会不会有问题?假设一个枚举有5项,兰德选择5项。Enum.ElementAt(5)将找不到任何内容,对吗?
rand.Next
从不选择其上限。
ints[randgen.Next(ints.Length)];
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);