【问题标题】:C#: Elegant code for getting a random value from an IEnumerableC#:从 IEnumerable 获取随机值的优雅代码
【发布时间】:2011-08-31 14:51:30
【问题描述】:

在 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 中获取随机值?

【问题讨论】:

标签: c#


【解决方案1】:

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

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&lt;T&gt;,这将是O(n),因为这是.Count() 和随机.ElementAt() 调用的复杂性;但是,数组和列表都是特例,所以在这些情况下,它都是 O(1)。

【讨论】:

  • 但是请注意,它会两次传递数据,如果数据集在调用Count 和调用ElementAt 之间发生变化,这可能会导致问题。 (这里不是问题,但可能是一般问题。)
  • 真; Jon Skeet 在stackoverflow.com/questions/648196/random-row-from-linq-to-sql/… 的回答可能是这方面最好的回答。
  • 找一个ElementAt(enum.Count())会不会有问题?假设一个枚举有 5 个项目,而 rand 选择 5。Enum.ElementAt(5) 不会找到任何东西,对吧?
  • rand.Next 从不选择其上限。
【解决方案2】:

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

编辑:这里的重点...

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

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

这是一个 O(1) 操作。

【讨论】:

  • “半随机”是什么意思?
  • 随机类生成伪随机数,而不是加密强随机数。
  • @Chris 我知道什么是伪随机。我不知道“半随机”应该是什么意思。并且伪随机并不妨碍它在加密上是安全的,例如:cryptographically strong != (!pseudo-random)
  • 这可能很简单,但代价高昂,因为它会对整个枚举进行排序。有众所周知的用于随机选择的一次性算法,它在枚举上递增操作。 stackoverflow.com/questions/48087/…
  • 伪随机!= 加密强度。如果你能猜到这个值,你就可以根据它来破解密码学。来自en.wikipedia.org/wiki/Strong_cryptography:“这个术语密码学上的强通常用于描述加密算法,并且与其他一些算法(因此密码学上较弱)相比,它意味着对攻击具有更大的抵抗力......在这种用法中,术语意味着难以猜测。”
【解决方案3】:

排序的效率会低得多。只需使用 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()));

【讨论】:

  • 最后一个括号是否应该在.First()之前?
  • 为什么使用.Skip(N).First() 而不是.ElementAt(N)
  • 你是绝对正确的。 ElementAt(N) 对于列表来说会更快。将进行相应的编辑。
【解决方案4】:

一些简单易读的东西怎么样:

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

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

【讨论】:

  • [] 不适用于 IEnumerable,这是发帖人要求的。我不认为张贴者想要一个基于数组或列表的解决方案,正如您所指出的,这很简单。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-08
  • 2015-03-22
  • 2016-09-20
  • 2021-06-30
  • 1970-01-01
  • 2015-01-27
  • 1970-01-01
相关资源
最近更新 更多