Вот еще одна идея:
using System;
using System.Collections.Generic;
using System.Linq;
namespace RandomElements
{
class Program
{
static IEnumerable<int> GetRandomElements(IEnumerable<int> source, int count)
{
var random = new Random();
var length = source.Count();
var enumerator = source.GetEnumerator();
if (length < count)
{
throw new InvalidOperationException("Seriously?");
}
while (count > 0)
{
const int bias = 5;
var next = random.Next((length / bias) - count - bias) + 1; // To make sure we don't starve.
length -= next;
while (next > 0)
{
if (!enumerator.MoveNext())
{
throw new InvalidOperationException("What, we starved out?");
}
--next;
}
yield return enumerator.Current;
--count;
}
}
static void Main(string[] args)
{
var sequence = Enumerable.Range(1, 100);
var random = GetRandomElements(sequence, 10);
random.ToList().ForEach(Console.WriteLine);
}
}
}
Перечисление нужно пройти только один раз (если вы передаете ICollection, то есть в противном случае ему нужно знать длину). Это может быть полезно, если обходить перечисление или копировать все элементы или что-либо еще дорого.
Я не статистик, не математик и не волшебник, так что не держите это против меня, но я обнаружил, что без «предвзятости», введенной в строке 22, я чувствовал, что вроде бы хотел выбрать больше из задней части последовательность. Возможно, кто-то мог бы подправить вероятности больше? Если перечисление действительно дорого, вы можете сделать его более смещенным вперед.
Комментарии приветствуются.