Генерация случайных чисел на C# или класс Random под микроскопом

В этой статье я хочу показать решения проблем и задач, которые часто возникают при работе с классом Random в C#.  Начнем с самого начала, класс Random мы используем для генерации псевдослучайных чисел. Давай создадим наш собственный класс MyRandom, который мы будем использовать на протяжении всей статьи. Создаем в нем приватную переменную _random типа Random, мы будем использовать её во всех следующих методах. В начальном состоянии наш класс выглядит вот так:

class MyRandom
{
	private Random _random = new Random();
}

Одной из типичных ошибок является постоянное создание нового экземпляра класса Random в цикле. Конструктор Random, не принимающий параметров, принимает значение текущей даты и времени как seed (начальное состояние). Итерации в цикле "пробегают" очень быстро, что время «не успеет измениться» по их окончании; таким образом, все экземпляры Random получат в качестве начального состояния одинаковое значение и поэтому возвратят одинаковое псевдослучайное число. Из этих соображений мы делаем наше поле _random статическим и в качестве параметра конструктора передаем количество миллисекунд после старта системы (Environment.TickCount).

class MyRandom
{
	private static int _seed = Environment.TickCount;
	// general instance
	private static Random _random = new Random(_seed);
}

Думаешь инициализация на этом закончилась? А вот и нет, класс Random является не потокобезопасным. Это значит, что мы столкнемся с огромной головной болью если экземпляр нашего класса будет использоваться в двух и более потоках. Давай исправим это

class MyRandom
{
	private static int _seed = Environment.TickCount;
	// thread safety instance
	private static ThreadLocal<Random> _random = new ThreadLocal<Random>(() =>
		new Random(Interlocked.Increment(ref _seed))
	);
}

Теперь наш класс выглядит "взрослым" и безопасным. У моих слушателей часто возникает вопрос - "как мне генерировать случайный enum?". Здесь все очень просто, предположим, что у тебя есть enum типа CarBrand

enum CarBrand
{
	BMW, Audi, Bentley
}

Метод, который будет генерировать случайный CarBrand должен найти минимальное и максимальное значение этого enum и сгенерировать случайные число (int) в этом диапазоне. Затем с помощью явного преобразования вернуть тип CarBrand

/// <summary>
/// Get random value for specific enum
/// </summary>
/// <returns></returns>
public CarBrand NextCarBrand()
{
	int max = (int)Enum.GetValues(typeof(CarBrand)).Cast<CarBrand>().Max();
	int min = (int)Enum.GetValues(typeof(CarBrand)).Cast<CarBrand>().Min();
	CarBrand randomBrand = (CarBrand)_random.Value.Next(min, max + 1);
	return randomBrand;
}

Я предпочитаю писать гибкие решения, поэтому давай напишем generic метод, который сможет сгенерировать любой enum

/// <summary>
/// Generate random, but it should starts from 0 and no gaps
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T NextGenericEnum<T>()
	where T : struct, IConvertible
{
	if (!typeof(T).IsEnum)
	{
		throw new ArgumentException("T must be an enumerated type");
	}
	T[] values = (T[])(Enum.GetValues(typeof(T)));
	T randomEnum = values[_random.Value.Next(0, values.Length)];
	return randomEnum;
}

В этом методе есть несколько ограничений: значения должны начинаться с 0 и между ними не должно быть пробелов (например enum со значениями 1, 2, 5 тебе не подойдет)

Для того, чтоб сгенерировать случайное значение типа bool, мы можем написать такой простой метод

/// <summary>
/// Generate TRUE or FALSE
/// </summary>
/// <returns></returns>
public bool NextBool()
{
	// as simple as possible
	return _random.Value.Next(0, 2) == 1;
}

Часто возникает задача сгенерировать случайное имя. Для решения этой задачи нам понадобиться массив заготовленных значений и на основании него мы получим результат. Находим 10-ть самых популярных имен и идем в бой.

/// <summary>
/// Generate random string based on predefined values
/// </summary>
/// <returns></returns>
public string NextName()
{
	string[] predefinedNames = new string[]
	{
		"Emma","Noah","Mia",
		"William","Jacob","Liam",
		"Alice","Asher","Michael"
	};
	int index = _random.Value.Next(0, predefinedNames.Length);
	return predefinedNames[index];
}

Теперь мы можем использовать наш класс не боясь поток и повторяющихся последовательностей

static void Main(string[] args)
{
	const int MaxGeneratedItems = 3;
	MyRandom myRandom = new MyRandom();
	// random specific enum
	Console.WriteLine("Random car's brands:");
	for (int i = 0; i < MaxGeneratedItems; i++)
	{
		CarBrand cb= myRandom.NextCarBrand();
		Console.WriteLine(cb);
	}
	// random generic enum
	Console.WriteLine("\nRandom colors:");
	for (int i = 0; i < MaxGeneratedItems; i++)
	{
		ConsoleColor cc = myRandom.NextGenericEnum<ConsoleColor>();
		Console.ForegroundColor = cc;
		Console.WriteLine(cc);
	}
	Console.ForegroundColor = ConsoleColor.Gray;
	// random bool value
	Console.WriteLine("\nRandom bool:");
	Console.WriteLine(myRandom.NextBool());
	// random string based on predefined values
	Console.WriteLine("\nRandom name:");
	string name = myRandom.NextName();
	Console.WriteLine(name);
	Console.ReadKey();
}

Полный исходный код находится в прикрепленном архив

P.S. Если генерация случайных числел, является основных и наиболее важным компонентом твоего приложения, то самый хороший вариант использовать АПИ сайта Random.org, который даст тебе "самые" случайные числа. Спасибо, что ты дочитал до конца.

Присоединяйся

Зарегестрируйся с помощью социальных сетей.

Публикуй

Опиши работу, прикрепи файлы и назначь цену.

Зарабатывай

Получай пассивный доход с продажи работ.

Тебе понадобится 5 минут для публикации работы на сайте.
Похожие работы
Скачать

бесплатно

csharp random numbers In depth.zip
34359
Оцени работу

рейтинг

Поделись работой с друзьями

Мы не грузим циферки, чтоб ты увидел контент как можно быстрее;

Комментарии (1)

dmytro

/ /

Оставить комментарий

Ты не можешь комментировать

Только зарегестрированые пользователи имеют возможность комментировать работы
Генерация случайных чисел на C# или класс Random под микроскопом
В этой статье можно найти ответы на следующие вопросы: как правильно инициализировать класс Random, как получить случайный enum, как генерировать случайную строку, как генерировать простой тип bool, как сделать Random потокобезопасным
Категория: Образование
Стоимость: Бесплатно