День сто пятнадцатый. #ЗаметкиНаПолях

Случайные числа в C#

Вокруг генерации случайных чисел всегда ходит много мифов и поэтому возникает много путаницы. Можно ли использовать простую конструкцию:

var random = new Random();

или же необходимо подключать криптографические библиотеки?

На самом деле в подавляющем большинстве случаев для генерации случайных (псевдослучайных) чисел достаточно следующего кода:

Random random = new Random();

Console.WriteLine(random.Next());



Однако есть несколько ограничений:

1. Указание числа посева (seed) в конструкторе класса Random приведёт к созданию одинаковых последовательностей случайных чисел, поэтому используйте конструктор без параметров. Например, следующий код будет генерировать одну и ту же последовательность при каждом запуске:

Random random = new Random(10010);

for (int i = 0; i < 10; i++)

{

Console.WriteLine(random.Next());

}

2. Создавайте объект Random только один раз. Если метод должен использовать случайное число, не создавайте новый объект Random внутри метода. Создайте его вне метода и передавайте в метод в качестве параметра:

Random random = new Random();

SomeMethod(random);

вместо

SomeMethod(new Random());

Это связано с пунктом 1. Конструктор класса Random без параметров берёт в качестве числа посева системное время в формате тактов процессора (1 такт = 100 наносекунд). Однако по природе своей реализации системные часы не различают разницу во времени примерно в 15 миллисекунд. Поэтому последовательные вызовы конструктора класса Random могут привести к созданию нескольких одинаковых случайных последовательностей.



Для случайного перемешивания небольших коллекций вполне подойдёт простой код LINQ:

var shuffeledList = somelist.OrderBy(x => random.Next());

Для больших коллекций есть более эффективные алгоритмы, например, Фишера-Йетса (здесь используем метод-расширение для IList):

private static Random rng = new Random();  

public static void Shuffle<T>(this IList<T> list)

{

int n = list.Count;

while (n > 1) {

n--;

int k = rng.Next(n + 1);

T value = list[k];

list[k] = list[n];

list[n] = value;

}

}

Использование:

List<Product> products = GetProducts();

products.Shuffle();



Для небольшого количества случаев, когда требуется криптографически безопасное случайное число (например, при создании случайного пароля), используйте класс RNGCryptoServiceProvider или наследуйте от System.Security.Cryptography.RandomNumberGenerator.



Источники:

- https://www.youtube.com/watch?v=kW84q8WOBdU

-
https://stackoverflow.com/questions/273313/randomize-a-listt/