Ура!
Я не просто так начал статью с этого возгласа. Свершилось, есть в арсенале программиста удобный инструмент, который позволяет получить случайное целое число в заданном интервале. Стандартные функции в других языках программирования генерировали дробное число в интервале от 0 до 1. Не слишком удобное число для использования, приходится дополнительно напрягаться, для получения случайной последовательности в заданном интервале, например, от 1 до 5.
Удобный инструмент нужно проверить, зачастую ГСЧ не слишком качественные и приходится дополнительно упражняться в математике, чтоб повысить случайность генерируемых чисел, во имя избавления от плохо перемешанных псевдопоследовательностей. Зачастую случайным числам мы доверяем секреты, финансовые средства, развлечения, моделирование, испытания ответственных АСУ систем, тестирование программ, поэтому очень важно иметь уникальную последовательность ряда с разнообразными заданными характеристиками от интервалов значений, до степени перемешивания чисел последовательности.
Приведу пример неудачной генерации случайных чисел. В далеком 1993 году, когда хорошим считался нецветной VGA монитор c разрешением 640х480, от него меньше уставали глаза программистов, была распространена СУБД Paradox. Жадины из Borland International решили заработать побольше денег и для сетевого доступа требовали ключ, состоящий из 10 цифр. Чем больше ключей ввел, тем больше пользователей могут одновременно подключится к базе.
Сейчас начну хвастаться своими успехами :). Разными способами ко мне попало 3 ключа и меня озарило, что последовательность псевдослучайная и плохо перемешанная. Без всякой техники, сложных вычислений и даже без калькулятора мне удалось подобрать любое количество этих ключей. Конечно компания Borland не понесла больших убытков, но раз уж занялся защитой, то делай это хорошо, либо не трать время на бесполезную работу. Не буду выводить из этого мораль, надеюсь становиться понятнее, что ГСЧ дело важное.
В защиту псевдослучайных последовательностей скажу, что иногда они необходимы, например, когда ключ защиты вычисляется по формуле и программа принимает решение об успешной верификации ключа в локальном режиме. В 1993 году интернет не был распространен и программистам приходилось придумывать алгоритмы последовательностей без проверки на сторонних серверах. Но хорошо перемешивать обязательно. При современном развитии сетей, верификацию серийного номера, как программ, так и оборудования, произвести стало в возможным с использованием серверов верификации. Данная система значительно повысила стойкость к подделкам, но не для кого не секрет, что несмотря на это все равно происходит несанкционированное использование программного обеспечения.
Вывод такой: замки и запоры ставят от честных людей.
Работает объект в платформе 1С исключительно просто, хотя есть некоторые особенности.
ГСЧ = Новый ГенераторСлучайныхЧисел( Инициализация ); //Здесь можно изменить работу геренатора случайных чисел, изменение Инициализации числа дает разные результаты
Возврат ГСЧ.СлучайноеЧисло(1, ВариантНастройки);
Если не задавать Инициализацию, тогда последовательности рождаются случайным образом, инициализируясь самостоятельно. Заданное число инициализации возвратит предсказуемую последовательность, что иногда бывает нужно и полезно. А если каждый раз задавать разное число, то получается весьма неплохое перемешивание последовательности чисел.
Для исследования работы генератора случайных чисел в 1С, создал пару обработок и чтоб было интереснее одна в виде игры, но игра позже, сначала дело.
Сгенерировано 500 случайных чисел, в интервале от 0 до 10000, при постоянной инициализации генератора функцией ТекущаяУниверсальнаяДатаВМиллисекундах(). На диаграмме видо хорошее распределение значений. Причудливый узор. По оси У величина значения числа, по оси Х номер итерации.
Положения точек довольно разряжены, они почти не липнут друг к другу, что очень хорошо, значит числа сгенерированы весьма разнообразные.
Искусственно создам плохой вариант диаграммы:
При хорошей генерации так быть не должно.
Настройки генератора случайных чисел очень важный момент. Их немного, но от них все зависит.
При данной настройке формируется последовательность из 500 чисел в интервале от 0 до 10000 и постоянной инициализации генератора новым числом.
Заглянем в сформированную таблицу значений, предварительно отсортировав ее по значениям, чтоб увидеть, есть ли повторяющиеся числа, выданные генератором.
Беглый визуальный просмотр отсортированного списка показал, что есть повторения, хотя вероятность его невысока. Задан широкий интервал чисел для генерации, но на 10 и 30 шаге числа повторились.
Делаем вывод: объект ГенераторСлучайныхЧисел может генерировать повторяющиеся числа.
Это иногда неприемлемо. Например, генерируем случайный номер документа, непоследовательно, для сообщения пользователю, который сможет открыть именно определенный документ. В этом случае повторения недопустимы, иначе при повторениях будет непонятно какой документ открывать.
Зачем нужна случайная нумерация? Допустим мы выдаем нашим партнерам номера документов к сданному в ремонт оборудованию. Можно выдавать номера сквозной последовательной нумерации, но клиент, обладая одним номером может посмотреть как минимум соседние и зная интервал нумерации может просмотреть все документы. В приведенном примере это не ахти какой секрет, но бывает нужно сохранить в тайне информацию из других документов.
При больших интервалах значений чисел генерации вероятность повторяющихся будет уменьшаться, но если событие может произойти, то оно непременно произойдет.
Решением может стать проверка сгенерированного числа на уникальность, но при очень длинных последовательностях это будет занимать много времени. Придется думать над более сложными перемешиваниями, но тем и интереснее задача;).
При выводе в диаграмму значений обработка начинает ощутимо тормозить, на моем компьютере после добавления 1000 значений, поэтому в настройках есть флажок "Не формировать диаграмму". При его установке скорость работы значительно возрастает при генерации длинных последовательностей, поэтому при исследованиях проявляйте осмотрительность задавая количество генерируемых чисел.
Генератору можно задать определенное значение инициализации, тогда сколько не нажимай генерировать, результат будет одинаковый. При установке поля обработки "Число инициализации ГСЧ" в 0, происходит псевдослучайная инициализация при генерации последовательности чисел.
Скорость работы генератора хорошая, к примеру 100 000 чисел сгенерировались меньше чем за 0,5 секунд.
Пример применения ГСЧ на маленьком интервале чисел, покажу на примере игры Камень, Ножницы, Бумага.
Правила простые: два игрока жестами показывают указанные предметы. У кого в данный момент сильнее фигура, тот и победил.
Камень побеждает Ножницы.
Ножницы побеждают Бумагу.
Бумага побеждает Камень.
В данном варианте все равнозначны и вероятности победы одинаковые.
Сыграв 99 игр, я произвел одинаковое количество нажатий на каждую фигуру, по 33 раза, это видно по правой нижней диаграмме. Компьютер выигрывал чаще меня, немного обидно. Компьютер чаще использовал бумагу, это видно по левой нижней диаграмме. График в середине показывает, что в выигрыше я не был. Красный график выигрышей компьютера выше зеленого (моих выигрышей).
Попытайте свою удачу! Несмотря на одинаковую вероятность выпадения фигур, результат всегда разный.
Статистика игр отображается в середине, в верху в розовой группе элементов.
Нажав мышкой на кнопку один раз, можно в дальнейшем пользоваться цифрами клавиатуры (не дополнительной), для выбора нужной фигуры. Нажатием на клавиши получается немного быстрее вводить данные, особенно если нужно провести неосмысленные игры для накопления статистики.
Усложним игру. К классическим фигурам добавим еще Колодец.
Камень побеждает Ножницы.
Ножницы побеждают Бумагу.
Бумага побеждает Камень и Колодец.
Колодец побеждает Камень и Ножницы.
В данном варианте появляется неравнозначность фигур и теоретически больше шансов победить, выбирая Бумагу и Колодец, так как в их арсенале победа над двумя коллегами фигурами. Проверим на практике:
Я нажимал только на сильные фигуры и выиграл. Теория подтвердилась практикой.
Нажатие только на слабые фигуры тоже дало свой результат, я проиграл.
Еще более сложный вариант игры при введении пятой фигуры, Огонь.
Камень побеждает Ножницы и Колодец проигрывает Бумаге и Огню.
Ножницы побеждают Бумагу и Огонь проигрывают Камню и Колодцу.
Бумага побеждает Камень и Колодец проигрывает Огню и Ножницам.
Колодец побеждает Огонь и Ножницы проигрывает Камню и Бумаге.
Огонь побеждает Бумагу и Камень проигрывает Ножницам и Колодцу.
В данном варианте фигуры равнозначны, но выигрывают и проигрывают двум фигурам, это вносит некоторую интригу и снижает вероятность ничьих.
Мои нажатия были хаотичными и компьютер меня опять обыграл. Во время игры видно какую фигуру компьютер использует чаще, исходя из этой информации можно попробовать играть не хаотично, а вдумчиво и возможно повысить свои шансы на успех.
Настройка генератора случайных чисел имеет свои особенности. При снятом флаге "Не использовать ответ пользователя" при инициализации ГСЧ используется номер нажатой игроком клавиши, что добавляет случайность генерации. Человек великолепный генератор случайных чисел, но все же у мозга тоже встречаются псевдопоследовательности. Если заставить человека произвести 100 нажатий на клавиши, то эта процедура может быть выполнена добросовестно, нажатием на как можно более разнообразные клавиши, а может халатно, нажатием только на одну кнопку. При установленном флаге работает только перемешивание компьютера.
При получении от человека случайной последовательности, например, для создания ключа электронной подписи, целесообразно учитывать не только нажатую клавишу, но и интервал между нажатиями. Это очень случайная информация, хотя хороший музыкант может сгенерировать несколько раз одну и туже последовательность.
Флаг "Не использовать инициализацию ГСЧ", включает/выключает режим инициализации. Если при создании объекта ГенераторСлучайныхЧисел не задать инициализацию, то генерируется перемешанная последовательность, я так понимаю, заложена некая инициализация, мне показалось вполне эффективная.
ГСЧ = Новый ГенераторСлучайныхЧисел();
Также, в обработке, можно задать свое число инициализации, тогда предсказуемо генерируется одно и тоже число.
При написании процедуры расчета выигрыша столкнулся с проблемой описания результата, особенно для игры с пятью фигурами. Прикинув понял, что будет много букв, так как много вариантов при таком количестве фигур. Комбинаторика подсказывает нам, что при трех фигурах у нас максимально есть 9 вариантов, при четырех 16 вариантов, то при пяти 25 вариантов. Откинув ничьи, это когда выбрана одинаковая фигура, я понял, что придется написать 19 вариантов условий.
Я задумался, так как явно получится плохо читаемый код, и нашел как я считаю красивое решение. Всего с 9 условиями.
Рез = Игрок - Комп;
Если ВариантНастройки = 3 Тогда
Если (Рез = -1) ИЛИ (Рез = 2) Тогда
Возврат 1; //Победа
Иначе
Возврат 2; //Поражение
КонецЕсли;
ИначеЕсли ВариантНастройки = 4 Тогда
Если (Рез = -1) ИЛИ (Рез = 2) ИЛИ (Рез = 3) Тогда
Возврат 1; //Победа
Иначе
Возврат 2; //Поражение
КонецЕсли;
ИначеЕсли ВариантНастройки = 5 Тогда
Если (Рез = -1) ИЛИ (Рез = 2) ИЛИ (Рез = -3) ИЛИ (Рез = 4) Тогда
Возврат 1; //Победа
Иначе
Возврат 2; //Поражение
КонецЕсли;
КонецЕсли;
Компактно, понятно, легко редактировать.
Я прикинул что каждая фигура имеет свой номер: 1 - Камень, 2 - Ножницы, 3 - Бумага, 4 - Колодец, 5 - Огонь. В результате игры я вычисляю разницу между номерами фигур, и эта разница дает мне однозначный ответ, на вопрос кто победил.
Обработки, помогающие исследовать свойства случайных последовательностей для управляемого приложения и написаны без привязки к конфигурации. Тестировались на платформе 8.3.10, 8.3.11 на тонком клиенте.
Данной статьей я надеялся донести важность и серьезное отношение при генерации последовательностей случайных чисел.