Недавно один из членов моей комманды задал мне вопрос: "Почему генерация случайных чисел в .Net упорно выдаёт не рэндомные значения, а повторяющиеся?". И последовательный вызов вот такого метода, всегда генерирует одну и ту же последовательность чисел:
1: const string AllowableCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
2:
3: public static string GenerateRandomString(int length)
4: {
5: string randomString = String.Empty;
6: Random random = new Random();
7: for (int i = 0; i < length; i++)
8: randomString += AllowableCharacters[random.Next(AllowableCharacters.Length)];
9:
10: return randomString;
11: }
Случай меня заинтересовал, и я полез в рефлектор чтобы понять причину такого поведения. Вот, что я там увидел:
1: public Random() : this(Environment.TickCount) {}
Наверное ни для кого не секрет, что генерация случайных чисел в .Net (и не только в .Net) базируется на использовании некоего начального значения, которое должно быть уникальным. Для получений таких значений часто используется уникальная, в данном контексте, метка времени. Как видно из кода, конструктор по умолчанию, в качестве такого, значения использует
Environment.TickCount - время, прошедшее со старта приложения, в миллисекундах. Т.е. получается, что если ваши объекты Random создаются с интервалом меньше миллиcекунды, при помощи конструктора по умолчанию – они будут инициализированы одним и тем же параметром, что в свою очередь влечёт генерацию одинаковых последовательностей "случайных" чисел.
На форумах можно встретить несколько вариантов решения проблемы, таких как передачу в конструктор более мелкого промежутка времени (например DateTime.Now.Ticks), задержка между вызовами методов при помощи Thread.Sleep. Но исходя из всего вышесказанного, наилучшим варантом, гарантирующим генерацию случайных чисел, является использование одного экземпляра класса Random:
1: public static string GenerateRealRandomString(int length, Random random)
2: {
3: string randomString = String.Empty;
4: for (int i = 0; i < length; i++)
5: randomString += AllowableCharacters[random.Next(AllowableCharacters.Length)];
6:
7: return randomString;
Где вызов метода, осуществляется таким образом:
1: Random random = new Random();
2: Console.WriteLine(GenerateRealRandomString(10, random));
3: Console.WriteLine(GenerateRealRandomString(10, random));
Материалы поста:
- Насколько случайным бывает Random
Ссылки по теме:
Denis Reznik
LPP Soft, .Net Team Lead
Kharkov, Ukraine