Разработка и Конфигурация · 12 мин
Как перемешать массив в 1С
Как перемешать массив в 1С: алгоритм Фишера–Йетса, генератор случайных чисел, готовые функции с примерами кода, типичные ошибки и проверка результата.
Во встроенном языке 1С нет готового метода для случайного перемешивания массива — ни у объекта Массив, ни у универсальных коллекций. Поэтому задача решается собственной процедурой на основе генератора случайных чисел. Чаще всего применяют алгоритм Фишера–Йетса: он даёт равномерное распределение перестановок и работает за линейное время, что важно при обработке больших массивов в серверных процедурах.
Перемешивание требуется в прикладных задачах чаще, чем кажется: случайный порядок вопросов в тестах сотрудников, жеребьёвка участников акции, формирование случайной выборки товаров для инвентаризации, перемешивание строк табличной части для проверочных расчётов. Во всех этих сценариях важно не только получить «случайный» порядок, но и понимать ограничения генератора случайных чисел платформы.
Ниже разобраны рабочие варианты кода: классический алгоритм Фишера–Йетса, упрощённый способ через удаление элементов, перемешивание таблицы значений и списка значений, а также проверка результата и типичные ошибки, из-за которых «случайность» оказывается предсказуемой.
Какой инструмент использовать: генератор случайных чисел
Основа любого перемешивания — объект ГенераторСлучайныхЧисел. Он создаётся конструктором с необязательным параметром-зерном: Новый ГенераторСлучайныхЧисел(). Если зерно не указано, платформа инициализирует генератор сама, и последовательность будет различаться между запусками. Если передать фиксированное число, последовательность станет воспроизводимой — это полезно в тестах, но вредно в прикладной логике жеребьёвки.
Метод СлучайноеЧисло(НижняяГраница, ВерхняяГраница) возвращает целое число в заданном диапазоне включительно. Именно здесь кроется частая ошибка: индексы массива в 1С начинаются с нуля, поэтому для массива из N элементов корректный диапазон — от 0 до N-1, а не от 1 до N.
Основной способ: алгоритм Фишера–Йетса
Это рекомендуемый вариант для любых массивов: он выполняется за один проход, не создаёт лишних коллекций и даёт математически корректное равномерное распределение. Процедура изменяет переданный массив «по месту», поэтому отдельного возврата значения не требуется.
Процедура ПеремешатьМассив(Массив) Экспорт
Генератор = Новый ГенераторСлучайныхЧисел();
ВерхнийИндекс = Массив.ВГраница();
Для Индекс = ВерхнийИндекс По 1 Цикл
СлучайныйИндекс = Генератор.СлучайноеЧисло(0, Индекс);
ВременноеЗначение = Массив[Индекс];
Массив[Индекс] = Массив[СлучайныйИндекс];
Массив[СлучайныйИндекс] = ВременноеЗначение;
КонецЦикла;
КонецПроцедуры
Обратите внимание на цикл «от большего индекса к меньшему» и на то, что случайный индекс выбирается в диапазоне от 0 до текущего, а не по всему массиву. Если выбирать позицию по всему массиву на каждом шаге, распределение перестановок перестаёт быть равномерным — некоторые варианты порядка будут выпадать чаще других. Для жеребьёвки или формирования тестов это уже некорректный результат.
Вызов выглядит так:
Товары = Новый Массив;
Товары.Добавить("Товар А");
Товары.Добавить("Товар Б");
Товары.Добавить("Товар В");
ПеремешатьМассив(Товары);
Генератор = Новый ГенераторСлучайныхЧисел();
Для Индекс = Массив.ВГраница() По 1 Цикл
Жребий = Генератор.СлучайноеЧисло(0, Индекс);
Тмп = Массив[Индекс];
Массив[Индекс] = Массив[Жребий];
Массив[Жребий] = Тмп;
КонецЦикла;Упрощённый вариант: случайное извлечение элементов
Для небольших массивов (десятки или сотни элементов) допустим более наглядный способ: последовательно извлекать случайный элемент из исходного массива и переносить его в новый. Код проще для чтения, но работает медленнее, потому что метод Удалить() сдвигает оставшиеся элементы.
Функция ПеремешатьКопию(ИсходныйМассив) Экспорт
Генератор = Новый ГенераторСлучайныхЧисел();
Остаток = Новый Массив;
Для Каждого Элемент Из ИсходныйМассив Цикл
Остаток.Добавить(Элемент);
КонецЦикла;
Результат = Новый Массив;
Пока Остаток.Количество() > 0 Цикл
Индекс = Генератор.СлучайноеЧисло(0, Остаток.ВГраница());
Результат.Добавить(Остаток[Индекс]);
Остаток.Удалить(Индекс);
КонецЦикла;
Возврат Результат;
КонецФункции
Такой вариант удобен, когда исходный порядок нужно сохранить — например, массив пришёл параметром и менять его нельзя по логике вызывающего кода. Но на массивах в десятки тысяч элементов разница в скорости станет заметной, и лучше вернуться к Фишеру–Йетсу.
Сравнение подходов
Быстрый, равномерное распределение, меняет исходный массив. Подходит для любых объёмов
Проще читается, сохраняет исходный массив, но медленнее на больших коллекциях
Генерация массива случайных ключей и сортировка по ним. Наглядно, но требует дополнительной структуры данных
Перемешивание таблицы значений и списка значений
В прикладном коде чаще встречаются не «голые» массивы, а таблицы значений и списки значений. Для таблицы значений применим тот же алгоритм с обменом строк, но менять местами нужно содержимое строк целиком — проще всего через промежуточный массив строк или через добавление служебной колонки со случайным числом и последующую сортировку по ней.
// Перемешивание таблицы значений через служебную колонку
ТЗ.Колонки.Добавить("СлучайныйКлюч");
Генератор = Новый ГенераторСлучайныхЧисел();
Для Каждого Строка Из ТЗ Цикл
Строка.СлучайныйКлюч = Генератор.СлучайноеЧисло(0, 1000000);
КонецЦикла;
ТЗ.Сортировать("СлучайныйКлюч");
ТЗ.Колонки.Удалить("СлучайныйКлюч");
Способ с колонкой-ключом корректен, если диапазон случайных чисел достаточно велик относительно числа строк — иначе совпадения ключей сделают порядок частично предсказуемым (строки с одинаковым ключом сохранят исходный взаимный порядок). Для списка значений удобно выгрузить элементы в массив, перемешать его Фишером–Йетсом и заполнить список заново.
- 1Добавьте служебную числовую колонку
- 2Заполните её значениями из генератора случайных чисел
- 3Отсортируйте таблицу по этой колонке
- 4Удалите служебную колонку
- 5Проверьте, что число строк и их состав не изменились
Проверка результата и контроль качества перемешивания
После перемешивания стоит убедиться в трёх вещах: состав элементов не изменился, количество элементов сохранилось, порядок действительно другой. Самая частая ошибка реализации — потеря или дублирование элемента при обмене, поэтому перед и после перемешивания полезно сравнить отсортированные копии массива.
Для проверки равномерности (например, в тестах общего модуля) можно прогнать перемешивание массива из нескольких элементов тысячи раз и посчитать, как часто каждый элемент оказывается на первой позиции. При корректной реализации частоты будут близки между собой; если один элемент систематически «лидирует», в алгоритме ошибка в диапазонах случайных индексов.
Как проверить результат
Совпадает с исходным
Элементы потеряны или продублированы
Отсортированные копии до и после совпадают
Появились новые или исчезли старые значения
Меняется от запуска к запуску
Порядок каждый раз одинаковый — генератор инициализирован фиксированным зерном
В серии прогонов позиции элементов примерно равновероятны
Один элемент стабильно чаще оказывается первым
Типичные ошибки и ограничения платформы
Первая группа ошибок связана с диапазонами: верхняя граница массива — это ВГраница(), то есть Количество() минус 1. Вызов СлучайноеЧисло(1, Массив.Количество()) приведёт к выходу за границу массива или к пропуску нулевого элемента. Вторая группа — создание генератора внутри цикла: при быстром последовательном создании генераторы могут инициализироваться близкими зернами, и «случайные» числа окажутся одинаковыми. Генератор создаётся один раз до цикла.
Третья ошибка — перемешивание массива, полученного из запроса, с последующей записью результата в базу без контроля. Если перемешанный порядок влияет на прикладные данные (очередность обработки, распределение товаров), зафиксируйте исходный и итоговый порядок в журнале — иначе воспроизвести и проверить операцию позже будет невозможно.
Отдельно учитывайте контекст исполнения. Генератор случайных чисел доступен и на сервере, и на клиенте, но в толстом и тонком клиенте поведение одинаково корректно только при соблюдении диапазонов. Если перемешивание выполняется на клиенте над большими данными, разумнее перенести операцию в серверную процедуру — заодно уменьшится трафик.
Выбор подхода под конкретную задачу
Если нужно просто перемешать массив в серверной процедуре — берите Фишера–Йетса без вариантов. Если исходный массив менять нельзя — используйте извлечение в новый массив. Если данные уже в таблице значений и дальше идут в отчёт или СКД — добавьте служебную колонку со случайным ключом и отсортируйте. Если нужна не полная перестановка, а случайная выборка N элементов из M — примените Фишера–Йетса только к первым N позициям, это стандартный приём частичного перемешивания.
Как выбрать решение
Частые вопросы
Есть ли в 1С готовый метод типа Перемешать()?
Нет. У объекта Массив есть только методы добавления, удаления, поиска, вставки и очистки. Перемешивание всегда реализуется собственной процедурой на базе ГенераторСлучайныхЧисел.
Почему при каждом запуске порядок получается одинаковым?
Скорее всего, генератор создан с фиксированным зерном: Новый ГенераторСлучайныхЧисел(123). Уберите параметр конструктора — тогда последовательность будет различаться между запусками.
Как перемешать строки табличной части документа?
Выгрузите табличную часть в таблицу значений методом Выгрузить(), перемешайте её через служебную колонку со случайным ключом, затем загрузите обратно методом Загрузить(). Учтите, что после этого документ потребуется записать, а изменение порядка строк может повлиять на проведение, если логика зависит от номеров строк.
Работает ли перемешивание на клиенте в управляемых формах?
Да, ГенераторСлучайныхЧисел доступен на клиенте. Но для массивов, полученных с сервера, и для больших объёмов данных эффективнее выполнять перемешивание в серверной процедуре и возвращать готовый результат.
Как получить случайный элемент массива без перемешивания?
Достаточно одного обращения к генератору: Элемент = Массив[Генератор.СлучайноеЧисло(0, Массив.ВГраница())]. Полное перемешивание для этой задачи избыточно.
Можно ли перемешать массив запросом?
Напрямую в языке запросов случайной сортировки нет. Практический обходной путь — выгрузить результат запроса в таблицу значений и перемешать её средствами встроенного языка.
Что важно запомнить
Готового метода перемешивания в 1С нет — используйте алгоритм Фишера–Йетса с одним генератором случайных чисел, созданным до цикла и без фиксированного зерна. Следите за диапазонами индексов (0..ВГраница), проверяйте сохранность состава массива и не применяйте воспроизводимые последовательности в операциях, влияющих на деньги или распределение благ.