Как получить только уникальные значения через Генератор Случайных Чисел в 1С

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

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

Анализ исходной проблемы

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


СЗ = Новый СписокЗначений;        
Для нум = 0 По 8 Цикл
    рандом = Новый ГенераторСлучайныхЧисел; // ОШИБКА: пересоздание в цикле
    данные = рандом.СлучайноеЧисло(0, 8);
    СЗ.Добавить(данные);
КонецЦикла;

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

Способ №1: Проверка вхождения перед добавлением

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


ВыбранныеЧисла = Новый Массив;
ГСЧ = Новый ГенераторСлучайныхЧисел;

Пока ВыбранныеЧисла.Количество() < 9 Цикл
    Случайное = ГСЧ.СлучайноеЧисло(0, 100);
    Если ВыбранныеЧисла.Найти(Случайное) = Неопределено Тогда
        ВыбранныеЧисла.Добавить(Случайное);
    КонецЕсли;
КонецЦикла;

Однако, разберем ситуацию, когда нам нужно получить все числа из диапазона от 0 до 8 в случайном порядке. Если мы будем использовать этот метод, то поиск последнего, девятого числа может затянуться: генератор будет выдавать уже существующие числа 1, 2, 3... и "попадание" в единственное оставшееся число может занять много итераций. Поэтому для получения случайных перестановок лучше использовать другой подход.

Способ №2: Случайное перемешивание (Алгоритм с удалением)

Выясним причину популярности этого метода. Мы заранее создаем массив со всеми возможными значениями, а затем "вытаскиваем" из него элементы случайным образом. Это гарантирует отсутствие дублей и стопроцентное заполнение результата за фиксированное количество шагов.

Рассмотрим пошагово реализацию:

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

МассивЧисел = Новый Массив;
Для инд = 0 по 8 Цикл
    МассивЧисел.Добавить(инд);
КонецЦикла;

ПеремешанныйМассив = Новый Массив;
ГСЧ = Новый ГенераторСлучайныхЧисел;

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

Способ №3: Оптимизированный алгоритм перемешивания (Фишер-Йетс)

Проанализируем производительность предыдущего метода. Метод Удалить() для массива в 1С заставляет систему сдвигать все элементы, стоящие после удаленного. На маленьких массивах это незаметно, но на тысячах элементов это станет проблемой. Профессиональный подход — использование алгоритма Тасования Фишера — Йетса.

Суть алгоритма заключается в том, что мы не удаляем элементы, а меняем их местами. Мы идем с конца массива к началу и меняем текущий элемент со случайным элементом, стоящим перед ним. Это работает максимально быстро (сложность O(n)).


МассивДанных = Новый Массив;
Для и = 0 По 100 Цикл
    МассивДанных.Добавить(и);
КонецЦикла;

ГСЧ = Новый ГенераторСлучайныхЧисел;
н = МассивДанных.Количество();
Пока н > 1 Цикл
    н = н - 1;
    к = ГСЧ.СлучайноеЧисло(0, н);
    // Меняем местами элементы
    ВременнаяПеременная = МассивДанных[к];
    МассивДанных[к] = МассивДанных[н];
    МассивДанных[н] = ВременнаяПеременная;
КонецЦикла;

Способ №4: Использование объектов «Соответствие» или «ТаблицаЗначений»

Посмотрим на альтернативные варианты, предложенные экспертами. Если нам нужно просто быстро проверить уникальность при генерации большого объема данных, объект Соответствие работает быстрее, чем поиск в массиве через Найти(), так как использует хеш-таблицы.


УникальныеДанные = Новый Соответствие;
ГСЧ = Новый ГенераторСлучайныхЧисел;

Пока УникальныеДанные.Количество() < 10 Цикл
    Число = ГСЧ.СлучайноеЧисло(1, 1000);
    УникальныеДанные.Вставить(Число, Истина); // Дубли просто перезапишут ключ
КонецЦикла;

Также можно использовать ТаблицаЗначений. Мы можем добавить все сгенерированные числа в колонку таблицы, а затем вызвать метод Свернуть("ИмяКолонки"). Платформа сама удалит все повторяющиеся строки, оставив только уникальные значения.

Технические тонкости работы с ГСЧ в 1С

Разберем важные нюансы, которые помогут избежать ошибок в крупных проектах:

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

← На главную