Программисты 1С нередко сталкиваются с ситуацией, которая на первый взгляд кажется мистической: код, генерирующий случайные числа, идеально работает в режиме отладки, но выдает абсолютно одинаковые значения в обычном режиме работы. Это создает серьезные проблемы при реализации алгоритмов распределения заказов, генерации проверочных кодов или в игровых механиках. Проанализируем, почему так происходит, и как правильно спроектировать генерацию случайных данных в платформе 1С.
Рассмотрим типичную ситуацию. Разработчик создает функцию, которая должна вернуть массив случайных чисел, и внутри цикла на каждой итерации создает новый объект ГенераторСлучайныхЧисел. При пошаговом проходе кода (через клавишу F10 в конфигураторе) массив заполняется корректно — числа действительно разные. Однако при запуске пользователем тот же самый код заполняет массив, например, десятью одинаковыми пятерками.
Причина кроется в механизме инициализации генератора. По умолчанию, если в конструктор Новый ГенераторСлучайныхЧисел() не передано конкретное число (зерно или "seed"), платформа инициализирует его значением текущего системного времени. В режиме отладки между выполнением строк кода проходят доли секунды или даже целые секунды, пока разработчик анализирует переменные. За это время системный таймер успевает обновиться, и каждый новый объект получает новое начальное значение.
В рабочем режиме сервер 1С выполняет итерации цикла за микросекунды. Скорость выполнения настолько высока, что системное время, которое считывает конструктор, не успевает измениться. В итоге создается несколько десятков объектов ГенераторСлучайныхЧисел, инициализированных абсолютно одинаковым числом. А так как алгоритм генерации псевдослучайных чисел детерминирован, первый вызов метода СлучайноеЧисло() у всех этих объектов выдаст один и тот же результат.
Проанализируем ситуацию на примере кода, который часто становится источником ошибок. Здесь генератор создается внутри вспомогательной функции, которая вызывается в цикле:
Функция СгенеритьСЧ(Предел) Экспорт
// ОШИБКА: Объект создается заново при каждом вызове
ГСЧ = Новый ГенераторСлучайныхЧисел;
Возврат ГСЧ.СлучайноеЧисло(1, Предел);
КонецФункции
Функция МассивСЧ(Количество, Предел) Экспорт
Результат = Новый Массив;
Для Нум = 1 по Количество Цикл
// Из-за высокой скорости вызова СгенеритьСЧ()
// ГСЧ внутри неё будет инициализирован одинаково
Результат.Добавить(СгенеритьСЧ(Предел));
КонецЦикла;
Возврат Результат;
КонецФункции
В данном примере на каждой итерации объект ГСЧ рождается, выдает число и уничтожается. Поскольку системное время не изменилось, "случайность" пропадает.
Самый простой и эффективный способ исправить ситуацию — создать объект ГенераторСлучайныхЧисел один раз и использовать его на протяжении всего процесса генерации. В этом случае после первого получения числа внутреннее состояние генератора меняется согласно алгоритму, и следующий вызов метода СлучайноеЧисло() гарантированно выдаст новое значение из последовательности.
Рассмотрим исправленный вариант кода:
Функция МассивСЧ(Количество, Предел) Экспорт
// Создаем генератор ОДИН раз перед началом цикла
ГСЧ = Новый ГенераторСлучайныхЧисел;
Результат = Новый Массив;
Для Нум = 1 по Количество Цикл
// Используем один и тот же объект
Результат.Добавить(ГСЧ.СлучайноеЧисло(1, Предел));
КонецЦикла;
Возврат Результат;
КонецФункции
Этот подход не только решает проблему одинаковых чисел, но и экономит ресурсы сервера, так как создание нового объекта в памяти — операция более затратная, чем вызов метода уже существующего объекта.
Иногда архитектура системы требует, чтобы генерация происходила в разных независимых функциях или даже разных модулях, где невозможно передать один и тот же объект ГСЧ. В этом случае мы можем принудительно задать начальное число для генератора.
Если мы передадим в конструктор Новый ГенераторСлучайныхЧисел(Число) уникальное значение, последовательность будет определяться именно им. Чтобы числа не повторялись, в качестве параметра можно использовать комбинацию текущего времени и, например, номера итерации или секунд текущей даты в миллисекундах (если доступны внешние средства получения точного времени).
Однако помните: если вы передадите в конструктор одно и то же число, вы всегда будете получать одну и ту же "случайную" последовательность. Это свойство иногда используют специально для тестирования алгоритмов, чтобы результаты были воспроизводимы.
Если в вашей конфигурации случайные числа требуются часто и в разных местах, рекомендуем придерживаться следующих правил:
ГенераторСлучайныхЧисел. Это гарантирует уникальность последовательности в рамках сеанса пользователя.ГСЧ в самом начале (например, в обработчике нажатия кнопки) и передавайте его "вниз" по стеку вызовов.Новый УникальныйИдентификатор(). Этот метод не зависит от скорости выполнения цикла и гарантирует статистическую уникальность без использования ГСЧ.Подводя итог, можно сказать, что "мистика" в 1С всегда имеет логическое техническое объяснение. Генератор случайных чисел — это математический инструмент, который требует аккуратного обращения с его жизненным циклом. Избегайте создания объекта внутри высоконагруженных циклов, и ваш код будет работать стабильно как в режиме отладки, так и в промышленной эксплуатации.