c# - 在 .NET 中模拟 Python 的 random.choice

标签 c# .net random

Python 的模块 'random' 有一个函数 random.choice

random.choice(seq)
Return a random element from the non-empty sequence seq. If seq is empty, raises IndexError.

我如何在 .NET 中模拟它?

public T RandomChoice<T> (IEnumerable<T> source)

编辑:几年前我听说这是一个面试问题,但今天这个问题自然而然地出现在我的工作中。面试问题陈述有限制

  • '序列太长,无法保存到内存中'
  • '你只能循环一次序列'
  • “序列没有长度/计数方法”(à la .NET IEnumerable)

最佳答案

要创建一个只迭代一次源的方法,并且不必分配内存来临时存储它,您可以计算迭代了多少项,并确定当前项应该是结果的概率:

public T RandomChoice<T> (IEnumerable<T> source) {
  Random rnd = new Random();
  T result = default(T);
  int cnt = 0;
  foreach (T item in source) {
    cnt++;
    if (rnd.Next(cnt) == 0) {
      result = item;
    }
  }
  return result;
}

当你在第一个项目时,应该使用它的概率是 1/1(因为这是你到目前为止看到的唯一项目)。当你在第二个项目时,它应该替换第一个项目的概率是 1/2,依此类推。


这自然会使用更多的 CPU,因为它会为每个项目创建一个随机数,而不仅仅是一个随机数来选择一个项目,正如 dasblinkenlight 指出的那样。您可以检查源代码是否实现了 IList<T> ,正如丹涛所建议的,并使用一个使用功能的实现来获取集合的长度并通过索引访问项目:

public T RandomChoice<T> (IEnumerable<T> source) {
  IList<T> list = source as IList<T>;
  if (list != null) {
    // use list.Count and list[] to pick an item by random
  } else {
    // use implementation above
  }
}

注意:您应该考虑发送 Random实例到方法中。否则,如果您两次调用该方法的时间太近,您将获得相同的随机种子,因为种子是从当前时间创建的。


测试运行的结果,从包含 0 - 9 的数组中选择一个数字,1000000 次,以表明所选数字的分布没有偏斜:

0: 100278
1: 99519
2: 99994
3: 100327
4: 99571
5: 99731
6: 100031
7: 100429
8: 99482
9: 100638

关于c# - 在 .NET 中模拟 Python 的 random.choice,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11314908/

相关文章:

c# - Random.Range 以及如何获取Random

c# - 如何解析 XML 文件

c# - 无需访问 Internet 的桌面客户端应用程序中的 Azure Application Insights

javascript - 为 JSON 元素生成 id

使用 Turtle 生成 Python 随机颜色

c# - BitmapSource 指向本地文件

c# - WPF 中的快速像素绘制

c# - 如何在内存中运行一些代码?

c# - 向基于模型的列表添加值

ios - 跟踪随机数