Как сгенерировать набор уникальных случайных чисел в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике программирования на платформе 1С:Предприятие часто возникает задача получения набора случайных чисел, которые не должны повторяться. Это может быть выборка случайных документов для проверки, распределение заявок между менеджерами (так называемый «холодный обзвон»), проведение лотерей или генерация уникальных идентификаторов. Несмотря на наличие встроенного объекта ГенераторСлучайныхЧисел, задача обеспечения уникальности требует правильного алгоритмического подхода. В этой статье мы подробно разберем, как эффективно решить эту задачу, избежав типичных ошибок новичков.

Основы работы с ГСЧ и главная ловушка

Прежде всего, проанализируем работу самого объекта ГенераторСлучайныхЧисел. Основная ошибка, которую совершают многие разработчики, заключается в инициализации объекта внутри цикла. Рассмотрим ситуацию: если вы создаете Новый ГенераторСлучайныхЧисел() на каждой итерации цикла, система инициализирует его, используя текущее системное время. Поскольку современные процессоры работают крайне быстро, время между итерациями может не измениться, и вы получите последовательность из одинаковых чисел.

Правило номер один: создавайте объект ГенераторСлучайныхЧисел один раз перед началом цикла. Внутри цикла используйте только метод СлучайноеЧисло() этого объекта.


// Неправильно:
Для Счетчик = 1 По 7 Цикл
    ГСЧ = Новый ГенераторСлучайныхЧисел(); // Инициализация в цикле даст одинаковые числа
    Число = ГСЧ.СлучайноеЧисло(1, 50);
КонецЦикла;

// Правильно:
ГСЧ = Новый ГенераторСлучайныхЧисел();
Для Счетчик = 1 По 7 Цикл
    Число = ГСЧ.СлучайноеЧисло(1, 50); // Используем один экземпляр
КонецЦикла;

Метод 1: Использование объекта Соответствие для контроля уникальности

Рассмотрим самый простой и эффективный с точки зрения написания кода способ. В 1С объект Соответствие (аналог Dictionary или Map в других языках) гарантирует уникальность своих ключей. Мы можем использовать это свойство: будем добавлять сгенерированные числа в качестве ключей, пока количество элементов в коллекции не достигнет нужного нам значения.

Проанализируем преимущества этого метода: нам не нужно вручную проверять, было ли число сгенерировано ранее. Если мы попытаемся вставить в Соответствие ключ, который там уже есть, старое значение просто перезапишется, а количество элементов (Количество()) не увеличится. Это избавляет нас от лишних условий Если ... Тогда.


ГСЧ = Новый ГенераторСлучайныхЧисел();
РезультатСоответствие = Новый Соответствие;
НужноеКоличество = 7;

Пока РезультатСоответствие.Количество() < НужноеКоличество Цикл
    СлучайноеЗначение = ГСЧ.СлучайноеЧисло(1, 50);
    // Вставляем число в ключ. Значение может быть любым, например, Истина
    РезультатСоответствие.Вставить(СлучайноеЗначение, Истина);
КонецЦикла;

// Выводим результат
Для Каждого Элемент Из РезультатСоответствие Цикл
    Сообщить(Элемент.Ключ);
КонецЦикла;

Метод 2: Проверка наличия в массиве (для небольших выборок)

Если вам нужно получить всего несколько чисел (как в нашем примере — 7 из 50), можно использовать обычный Массив и метод Найти(). Этот способ интуитивно понятен, но стоит помнить о производительности: поиск в массиве осуществляется линейно. Для 7 чисел это незаметно, но для выборки в тысячи элементов этот метод станет «узким местом» программы.


МассивРезультат = Новый Массив;
ГСЧ = Новый ГенераторСлучайныхЧисел();

Пока МассивРезультат.Количество() < 7 Цикл
    Число = ГСЧ.СлучайноеЧисло(1, 50);
    Если МассивРезультат.Найти(Число) = Неопределено Тогда
        МассивРезультат.Добавить(Число);
        Сообщить("Найдено уникальное число: " + Число);
    КонецЕсли;
КонецЦикла;

Метод 3: Выборка из предзаполненного массива (метод исключения)

Этот метод считается самым надежным и быстрым, если количество требуемых чисел приближается к размеру диапазона (например, нужно выбрать 45 чисел из 50). Вместо того чтобы «угадывать» числа и проверять их на уникальность, мы заранее создаем список всех возможных вариантов, а затем случайным образом извлекаем их оттуда.

Разберем алгоритм по шагам:

  1. Создаем массив и заполняем его всеми числами от 1 до 50.
  2. Генерируем случайный индекс в диапазоне от 0 до текущего размера массива минус один.
  3. Берем значение по этому индексу и добавляем в результат.
  4. Удаляем использованный элемент из исходного массива, чтобы он не попал в выборку снова.

// Подготовка исходного списка
СписокКандидатов = Новый Массив;
Для н = 1 По 50 Цикл
    СписокКандидатов.Добавить(н);
КонецЦикла;

ГСЧ = Новый ГенераторСлучайныхЧисел();
ФинальныйМассив = Новый Массив;
КоличествоНужных = 7;

Для Счетчик = 1 По КоличествоНужных Цикл
    // Генерируем ИНДЕКС, а не само число
    ТекущийВерхнийИндекс = СписокКандидатов.Количество() - 1;
    СлучайныйИндекс = ГСЧ.СлучайноеЧисло(0, ТекущийВерхнийИндекс);
    
    // Получаем значение и переносим в результат
    Значение = СписокКандидатов[СлучайныйИндекс];
    ФинальныйМассив.Добавить(Значение);
    
    // Удаляем из кандидатов, чтобы исключить повтор
    СписокКандидатов.Удалить(СлучайныйИндекс);
КонецЦикла;

Сравнение эффективности и нюансы реализации

Выясним причину, почему важно выбирать правильный метод. Представьте, что вам нужно выбрать 49 уникальных чисел из 50 возможных. Если использовать Метод 2 (Поиск в массиве), то при поиске последнего, 49-го числа, шанс «попасть» в него составляет всего 1/50 (2%). Программа будет совершать десятки «холостых» циклов, тратя процессорное время впустую. В то время как Метод 3 (Исключение) отработает ровно за 49 итераций без единого лишнего действия.

Также стоит упомянуть вопрос криптографической стойкости. Стандартный ГенераторСлучайныхЧисел в 1С является псевдослучайным. Это значит, что при одинаковом начальном числе (Seed) он всегда будет выдавать одну и ту же последовательность. Если вы хотите, чтобы последовательность была максимально непредсказуемой, инициализируйте ГСЧ значением миллисекунд текущей даты:

ГСЧ = Новый ГенераторСлучайныхЧисел(ТекущаяУниверсальнаяДатаВМиллисекундах());

Альтернативный подход через Запросы (SQL)

Если задача стоит выбрать 7 случайных ссылок на объекты (например, контрагентов) из базы данных, то проще и быстрее сделать это запросом. В языке запросов 1С нет функции RAND(), но есть хитрость: можно выбрать все записи, поместить их во временную таблицу, добавить поле с уникальным идентификатором (GUID) и отсортировать по нему. Поскольку GUID генерируются достаточно хаотично, это даст эффект случайной выборки.


// Пример логики запроса:
// ВЫБРАТЬ ПЕРВЫЕ 7 Ссылка ИЗ Справочник.Контрагенты УПОРЯДОЧИТЬ ПО АВТОНОМЕР

Однако помните, что АВТОНОМЕР или сортировка по УникальныйИдентификатор не гарантируют математически идеальную случайность, но для бизнес-задач, таких как обзвон клиентов, этого вполне достаточно.

Подводя итог, можно сказать: для простых задач используйте Соответствие, для сложных выборок из больших диапазонов — метод исключения из массива. Это обеспечит вашему коду стабильность, быстродействие и отсутствие трудноуловимых ошибок с повторяющимися данными.

← На главную