В практике разработчика 1С периодически возникают задачи, связанные с рандомизацией данных. Это может быть перемешивание вопросов в тесте, случайный выбор победителя в лотерее или просто необходимость отобразить список строк пользователю в непредсказуемой последовательности. В этой статье мы подробно разберем несколько способов реализации этой задачи, проанализируем сильные и слабые стороны каждого метода и выясним, как правильно работать со встроенными механизмами платформы «1С:Предприятие».
Рассмотрим самый простой и интуитивно понятный способ, который был предложен в обсуждении на форуме. Суть метода заключается в том, что мы помещаем все наши строки в объект Массив, а затем в цикле случайным образом выбираем индекс элемента, выводим его и удаляем из коллекции.
Проанализируем пример кода, реализующий этот алгоритм:
МассивСтрок = Новый Массив();
МассивСтрок.Добавить("Сообщение 1");
МассивСтрок.Добавить("Сообщение 2");
МассивСтрок.Добавить("Сообщение 3");
ГенераторСлучайныхЧисел = Новый ГенераторСлучайныхЧисел();
Пока МассивСтрок.Количество() > 0 Цикл
// Генерируем случайный индекс от 0 до максимального индекса текущего массива
ИндексСтроки = ГенераторСлучайныхЧисел.СлучайноеЧисло(0, МассивСтрок.Количество() - 1);
// Выводим строку пользователю
Сообщить(МассивСтрок[ИндексСтроки]);
// Удаляем элемент, чтобы он не попался снова
МассивСтрок.Удалить(ИндексСтроки);
КонецЦикла;
Разберем по шагам, что здесь происходит. Сначала мы инициализируем объект ГенераторСлучайныхЧисел. Обратите внимание, что мы создаем его без параметров — это важно, так как в этом случае платформа использует системное время для инициализации начального числа (сида). Далее мы запускаем цикл, который выполняется, пока в массиве есть хотя бы один элемент. На каждой итерации метод СлучайноеЧисло возвращает нам позицию строки, которую нужно вывести. После вывода мы вызываем метод Удалить.
Важный нюанс: этот метод хорошо подходит для небольших списков (до нескольких сотен строк). Однако, если строк будут тысячи, метод Удалить станет «узким местом». При удалении элемента из середины массива 1С вынуждена сдвигать все последующие элементы, что требует значительных ресурсов процессора при больших объемах данных.
Чтобы избежать затратных операций удаления и сделать код более профессиональным, проанализируем алгоритм перемешивания «на месте». Это классический метод, который работает за один проход по массиву и не требует создания дополнительных коллекций.
Посмотрим на пример реализации:
// Предположим, МассивСтрок уже заполнен данными
ГСЧ = Новый ГенераторСлучайныхЧисел();
ВерхняяГраница = МассивСтрок.Количество() - 1;
Для Счётчик = 0 По ВерхняяГраница - 1 Цикл
// Выбираем случайный индекс из оставшейся части массива
СлучайныйИндекс = ГСЧ.СлучайноеЧисло(Счётчик, ВерхняяГраница);
// Меняем элементы местами
ВременнаяПеременная = МассивСтрок[Счётчик];
МассивСтрок[Счётчик] = МассивСтрок[СлучайныйИндекс];
МассивСтрок[СлучайныйИндекс] = ВременнаяПеременная;
КонецЦикла;
// Теперь массив перемешан, можем просто вывести его в цикле
Для Каждого Строка Из МассивСтрок Цикл
Сообщить(Строка);
КонецЦикла;
Этот способ является наиболее эффективным с точки зрения использования памяти и скорости работы. Мы просто проходим по массиву и меняем текущий элемент со случайно выбранным элементом из «хвоста» списка. В результате сложность алгоритма составляет O(n), где n — количество строк.
В среде 1С часто удобнее использовать объект ТаблицаЗначений, особенно если данные приходят из запроса или нужно выполнить сортировку. Выясним причину популярности этого метода: он позволяет переложить задачу сортировки на оптимизированные внутренние алгоритмы платформы.
Разберем алгоритм действий:
ТаблицаЗначений и добавляем две колонки: «Текст» и «СлучайныйВес».Сортировать("СлучайныйВес").Пример кода для реализации этого подхода:
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Текст", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("СлучайныйВес", Новый ОписаниеТипов("Число"));
ГСЧ = Новый ГенераторСлучайныхЧисел();
// Заполнение (пример)
Для Инд = 1 По 10 Цикл
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Текст = "Строка №" + Инд;
НоваяСтрока.СлучайныйВес = ГСЧ.СлучайноеЧисло(0, 1000000);
КонецЦикла;
// Магия перемешивания одной строкой
ТЗ.Сортировать("СлучайныйВес");
// Вывод результата
Для Каждого Стр Из ТЗ Цикл
Сообщить(Стр.Текст);
КонецЦикла;
Этот метод считается наиболее «красивым» в контексте 1С, так как он легко читается и расширяется. Например, если вам нужно перемешать не просто строки, а целые структуры данных, вы просто добавляете нужные колонки в таблицу.
Рассмотрим важный технический аспект, который часто упускают начинающие разработчики. Объект ГенераторСлучайныхЧисел не является истинно случайным, это псевдослучайный генератор. Его поведение напрямую зависит от начального числа (seed).
Важные правила работы с ГСЧ:
Новый ГенераторСлучайныхЧисел() внутри цикла. Если цикл выполняется быстро (в пределах одной миллисекунды), генератор будет инициализироваться с одним и тем же значением системного времени, и вы получите последовательность одинаковых «случайных» чисел.Новый ГенераторСлучайныхЧисел(12345). В этом случае порядок строк при каждом запуске будет одинаковым, но визуально случайным.Если по каким-то причинам вы не хотите использовать ГенераторСлучайныхЧисел, существует альтернативный метод — использование объекта УникальныйИдентификатор. Строковое представление GUID (например, "550e8400-e29b-41d4-a716-446655440000") обладает свойствами высокой энтропии.
Мы можем модифицировать метод с таблицей значений, записывая в колонку сортировки результат вызова Строка(Новый УникальныйИдентификатор()). Сортировка по такой строковой колонке даст эффект случайного распределения. Однако помните, что генерация GUID — операция более тяжелая для системы, чем получение случайного числа, поэтому для очень больших массивов данных этот метод лучше не использовать.
Подводя итог, проанализируем ситуацию: для простых задач вывода сообщений идеально подходит метод тасования Фишера — Йетса в массиве. Если же данные уже находятся в табличной части или требуют сложной обработки, лучше воспользоваться методом сортировки ТаблицаЗначений по колонке со случайными весами.