Подтвердить что ты не робот

С#: Элегантный код для получения случайного значения из IEnumerable

В Python я могу это сделать:

>>> import random
>>> ints = [1,2,3]
>>> random.choice(ints)
3

В С# первое, что я сделал, это:

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?

4b9b3361

Ответ 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<T> это будет O (n), так как это сложность .Count() и случайный вызов .ElementAt(); однако, как особый случай для массивов и списков, поэтому в этих случаях это будет O (1).

Ответ 2

Нет, это самый простой способ. Конечно, это только полуслучайный, но я думаю, что он подходит для большинства потребностей.

EDIT: Огромная точка здесь...

Если вы хотите только одно значение, выбранное случайным образом из списка... тогда просто сделайте следующее:

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

Это операция O (1).

Ответ 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()));

Ответ 4

Как насчет чего-то простого и читаемого:

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

Серьезно, зачем обманывать свой код с помощью lambdas.OrderBy и .First и .Skip и т.д.?

Ответ 5

Это был лучший способ для меня

var myList = myIEnumerable.ToList();  //convert IEnumerable to List
myList .Shuffle();  //shuffle it
var randomObject = myList .First(); //grab the first in list