我找到了这篇文章:
但这意味着,为了接近样本中的真正随机性,我必须遍历所有元素,用随机数将它们放入内存中,然后排序。我这里有很多项目(数百万) - 有没有更有效的方法来解决这个问题?
我找到了这篇文章:
但这意味着,为了接近样本中的真正随机性,我必须遍历所有元素,用随机数将它们放入内存中,然后排序。我这里有很多项目(数百万) - 有没有更有效的方法来解决这个问题?
我建议简单地改组元素,就好像您正在编写修改后的 Fisher-Yates 改组一样,但只打扰改组第一个k
元素。例如:
public static void PartialShuffle<T>(IList<T> source, int count, Random random)
{
for (int i = 0; i < count; i++)
{
// Pick a random element out of the remaining elements,
// and swap it into place.
int index = i + random.Next(source.Count - i);
T tmp = source[index];
source[index] = source[i];
source[i] = tmp;
}
}
调用此方法后,第一个count
元素将从原始列表中随机选择元素。
请注意,我已将 指定Random
为参数,以便您可以重复使用相同的参数。不过要小心线程——有关更多信息,请参阅我关于随机性的文章。
如果元素可以在内存中,则先将它们放入内存中
List<Element> elements = dbContext.Select<Element>();
现在你知道元素的数量了。创建一组唯一索引。
var random = new Random();
var indexes = new HashSet<int>();
while (indexes.Count < k) {
indexes.Add(random.Next(elements.Count));
}
现在您可以从列表中读取元素
var randomElements = indexes.Select(i => elements[i]);
我假设数据库包含独特的元素。如果不是这种情况,您将不得不在从数据库查询时创建一个HashSet<Elements>
替代或追加。.Distinct()
更新
正如 Patricia Shanahan 所说,如果 k 与 n 相比较小,则此方法将很有效。如果不是这样,我建议选择一组要排除的 n - k 个索引
var random = new Random();
var indexes = new HashSet<int>();
IEnumerable<Element> randomElements;
if (k <= elements.Count / 2) {
while (indexes.Count < k) {
indexes.Add(random.Next(elements.Count));
}
randomElements = indexes.Select(i => elements[i]);
} else {
while (indexes.Count < elements.Count - k) {
indexes.Add(random.Next(elements.Count));
}
randomElements = elements
.Select((e,i) => indexes.Contains(i) ? null : elements[i])
.Where(e => e != null);
}
看看这个扩展方法http://extensionmethod.net/csharp/ienumerable-t/shuffle。您可以添加 Skip() Take() 类型以将值从最终列表中分页。