要创建一个只迭代一次源的方法,并且不必分配内存来临时存储它,你计算你迭代了多少项目,并确定当前项目应该是结果的概率:
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,依此类推。
正如 dasblinkenlight 指出的那样,这自然会使用更多的 CPU,因为它会为每个项目创建一个随机数,而不仅仅是一个随机数来选择一个项目。您可以IList<T>
按照 Dan Tao 的建议检查源是否实现了 ,并使用使用这些功能获取集合长度并按索引访问项目的实现:
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