Каков наилучший способ рандомизировать порядок универсального списка в C #? У меня есть конечный набор из 75 номеров в списке, которому я хотел бы назначить случайный заказ, чтобы нарисовать их для приложения типа лотереи.
c#
generic-list
mirezus
источник
источник
Ответы:
Перемешайте все
(I)List
с помощью метода расширения, основанного на перемешивании Фишера-Йейтса :Применение:
Приведенный выше код использует критикуемый метод System.Random для выбора кандидатов на своп. Это быстро, но не так случайно, как должно быть. Если вам нужно более высокое качество случайности в случайном порядке, используйте генератор случайных чисел в System.Security.Cryptography следующим образом:
Простое сравнение доступно в этом блоге (WayBack Machine).
Изменить: После написания этого ответа пару лет назад, многие люди комментировали или писали мне, чтобы указать на большой глупый недостаток в моем сравнении. Они конечно правы. Нет ничего плохого в System.Random, если он используется так, как задумано. В моем первом примере, приведенном выше, я создаю экземпляр переменной rng внутри метода Shuffle, который вызывает проблемы, если метод будет вызываться повторно. Ниже приведен фиксированный, полный пример, основанный на действительно полезном комментарии, полученном сегодня от @weston здесь, на SO.
Program.cs:
источник
Random rng = new Random();
это,static
можно решить проблему в посте сравнения. Поскольку каждый последующий вызов будет следовать из предыдущих вызовов последний случайный результат.Если нам нужно только перетасовать элементы в совершенно случайном порядке (просто чтобы смешать элементы в списке), я предпочитаю этот простой, но эффективный код, который упорядочивает элементы по guid ...
источник
var shuffledcards = cards.OrderBy(a => rng.Next());
compilr.com/grenade/sandbox/Program.csNewGuid
только гарантирует, что он дает вам уникальный GUID. Это не дает никаких гарантий относительно случайности. Если вы используете GUID для другой цели, а не для создания уникального значения, вы делаете это неправильно.Я немного удивлен всеми неуклюжими версиями этого простого алгоритма здесь. Фишер-Йейтс (или Кнут Шаффл) немного сложнее, но очень компактен. Почему это сложно? Потому что вам нужно обратить внимание на то,
r(a,b)
возвращает ли ваш генератор случайных чисел значение, гдеb
оно включено или исключено. Я также отредактировал описание Википедии, чтобы люди не слепо следовали псевдокоду и создавали трудно обнаруживаемые ошибки. Для .NetRandom.Next(a,b)
возвращает число, исключающее,b
так что без лишних слов, вот как это может быть реализовано в C # /. Net:Попробуйте этот код .
источник
i = list.Count - 1
, т.е. последняя итерация, яrnd.Next(i, list.Count)
верну вас. Поэтому вам нужноi < list.Count -1
как условие цикла. Ну, это вам не нужно, но это экономит 1 итерацию;)Метод расширения для IEnumerable:
источник
OrderBy
использует вариант QuickSort для сортировки элементов по их (якобы случайным) ключам. Производительность быстрой сортировки - O (N log N) ; напротив, тасование Фишера-Йейтса - это O (N) . Для коллекции из 75 элементов это может не иметь большого значения, но разница станет заметной для больших коллекций.Random.Next()
может привести к разумно псевдослучайному распределению значений, но это не гарантирует, что значения будут уникальными. Вероятность дублирования ключей растет (нелинейно) с N до тех пор, пока не достигнет определенности, когда N достигнет 2 ^ 32 + 1.OrderBy
QuickSort является стабильным родом; таким образом, если нескольким элементам присвоено одно и то же значение псевдослучайного индекса, их порядок в выходной последовательности будет таким же, как во входной последовательности; таким образом, смещение вводится в «перемешивание».Идея состоит в том, чтобы получить анонимный объект с элементом и случайным порядком, а затем изменить порядок элементов по этому порядку и вернуть значение:
источник
источник
var listCopy = list.ToList()
чтобы не выталкивать все элементы из входящего списка? Я действительно не понимаю, почему вы хотите изменить эти списки, чтобы очистить.РЕДАКТИРОВАТЬ Это
RemoveAt
слабость в моей предыдущей версии. Это решение преодолевает это.Обратите внимание на необязательный параметр
Random generator
: если реализация базовой платформыRandom
не является поточно-ориентированной или криптографически надежной для ваших нужд, вы можете внедрить свою реализацию в операцию.В
Random
этом ответе можно найти подходящую реализацию для поточно-безопасной криптографической реализации.Вот идея, расширяющая IList (надеюсь) эффективным способом.источник
GetNext
илиNext
?Вы можете добиться этого, используя этот простой метод расширения
и вы можете использовать его, выполнив следующие
источник
Random
экземпляр класса вне функции какstatic
переменную. В противном случае вы можете получить то же самое рандомизированное начальное число из таймера, если вызывается в быстрой последовательности.Это мой предпочтительный метод случайного воспроизведения, когда желательно не изменять оригинал. Это вариант алгоритма Фишера-Йейтса «наизнанку», который работает с любой перечисляемой последовательностью (длина
source
не должна быть известна с самого начала).Этот алгоритм также может быть реализован путем выделения диапазона от и
0
доlength - 1
случайного исчерпания индексов путем замены произвольно выбранного индекса на последний индекс, пока все индексы не будут выбраны ровно один раз. Этот код выше выполняет то же самое, но без дополнительного выделения. Который довольно опрятен.Что касается
Random
класса, это генератор чисел общего назначения (и если бы я проводил лотерею, я бы подумал об использовании чего-то другого). По умолчанию также используется начальное значение времени. Небольшое облегчение проблемы состоит в том, чтобы заполнитьRandom
класс с помощью методаRNGCryptoServiceProvider
или, который вы могли бы использоватьRNGCryptoServiceProvider
в методе, подобном этому (см. Ниже), для генерации равномерно выбранных случайных двойных значений с плавающей запятой, но для проведения лотереи в значительной степени требуется понимание случайности и природы источник случайности.Смысл генерации случайного двойного числа (исключительно между 0 и 1) заключается в использовании для масштабирования до целочисленного решения. Если вам нужно выбрать что-то из списка, основанного на случайном двойном значении
x
, это всегда будет0 <= x && x < 1
просто.Наслаждайтесь!
источник
Если вы не возражаете против использования двух
Lists
, то это, вероятно, самый простой способ сделать это, но, вероятно, не самый эффективный или непредсказуемый:источник
Если у вас есть фиксированное число (75), вы можете создать массив из 75 элементов, а затем перечислить ваш список, перемещая элементы в рандомизированные позиции в массиве. Вы можете сгенерировать отображение номера списка на индекс массива, используя случайную последовательность Фишера-Йейтса .
источник
Я обычно использую:
источник
источник
Вот эффективный Shuffler, который возвращает байтовый массив перемешанных значений. Это никогда не тасует больше, чем нужно. Его можно перезапустить с того места, где он ранее остановился. Моя фактическая реализация (не показана) - это компонент MEF, который позволяет заданную пользователем замену shuffler.
`
источник
Вот потокобезопасный способ сделать это:
источник
источник
Простая модификация принятого ответа, которая возвращает новый список вместо того, чтобы работать на месте, и принимает более общий,
IEnumerable<T>
как это делают многие другие методы Linq.источник
Я нашел интересное решение онлайн.
Предоставлено: https://improveandrepeat.com/2018/08/a-simple-way-to-shuffle-your-lists-in-c/
var shuffled = myList.OrderBy (x => Guid.NewGuid ()). ToList ();
источник
Старый пост, конечно, но я просто использую GUID.
GUID всегда уникален, и поскольку он обновляется каждый раз, когда результат меняется каждый раз.
источник
Очень простой подход к решению этой проблемы заключается в использовании нескольких случайных элементов в списке.
В псевдокоде это будет выглядеть так:
источник