К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как перемешать массив в 1С

Как перемешать массив в 1С: алгоритм Фишера–Йетса, генератор случайных чисел, готовые функции с примерами кода, типичные ошибки и проверка результата.

Во встроенном языке 1С нет готового метода для случайного перемешивания массива — ни у объекта Массив, ни у универсальных коллекций. Поэтому задача решается собственной процедурой на основе генератора случайных чисел. Чаще всего применяют алгоритм Фишера–Йетса: он даёт равномерное распределение перестановок и работает за линейное время, что важно при обработке больших массивов в серверных процедурах.

Перемешивание требуется в прикладных задачах чаще, чем кажется: случайный порядок вопросов в тестах сотрудников, жеребьёвка участников акции, формирование случайной выборки товаров для инвентаризации, перемешивание строк табличной части для проверочных расчётов. Во всех этих сценариях важно не только получить «случайный» порядок, но и понимать ограничения генератора случайных чисел платформы.

Ниже разобраны рабочие варианты кода: классический алгоритм Фишера–Йетса, упрощённый способ через удаление элементов, перемешивание таблицы значений и списка значений, а также проверка результата и типичные ошибки, из-за которых «случайность» оказывается предсказуемой.

Какой инструмент использовать: генератор случайных чисел

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

Метод СлучайноеЧисло(НижняяГраница, ВерхняяГраница) возвращает целое число в заданном диапазоне включительно. Именно здесь кроется частая ошибка: индексы массива в 1С начинаются с нуля, поэтому для массива из N элементов корректный диапазон — от 0 до N-1, а не от 1 до N.

Основной способ: алгоритм Фишера–Йетса

Это рекомендуемый вариант для любых массивов: он выполняется за один проход, не создаёт лишних коллекций и даёт математически корректное равномерное распределение. Процедура изменяет переданный массив «по месту», поэтому отдельного возврата значения не требуется.

Процедура ПеремешатьМассив(Массив) Экспорт

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

ВерхнийИндекс = Массив.ВГраница();

Для Индекс = ВерхнийИндекс По 1 Цикл

СлучайныйИндекс = Генератор.СлучайноеЧисло(0, Индекс);

ВременноеЗначение = Массив[Индекс];

Массив[Индекс] = Массив[СлучайныйИндекс];

Массив[СлучайныйИндекс] = ВременноеЗначение;

КонецЦикла;

КонецПроцедуры

Обратите внимание на цикл «от большего индекса к меньшему» и на то, что случайный индекс выбирается в диапазоне от 0 до текущего, а не по всему массиву. Если выбирать позицию по всему массиву на каждом шаге, распределение перестановок перестаёт быть равномерным — некоторые варианты порядка будут выпадать чаще других. Для жеребьёвки или формирования тестов это уже некорректный результат.

Вызов выглядит так:

Товары = Новый Массив;

Товары.Добавить("Товар А");

Товары.Добавить("Товар Б");

Товары.Добавить("Товар В");

ПеремешатьМассив(Товары);

bsl
Генератор = Новый ГенераторСлучайныхЧисел();
Для Индекс = Массив.ВГраница() По 1 Цикл
	Жребий = Генератор.СлучайноеЧисло(0, Индекс);
	Тмп = Массив[Индекс];
	Массив[Индекс] = Массив[Жребий];
	Массив[Жребий] = Тмп;
КонецЦикла;

Упрощённый вариант: случайное извлечение элементов

Для небольших массивов (десятки или сотни элементов) допустим более наглядный способ: последовательно извлекать случайный элемент из исходного массива и переносить его в новый. Код проще для чтения, но работает медленнее, потому что метод Удалить() сдвигает оставшиеся элементы.

Функция ПеремешатьКопию(ИсходныйМассив) Экспорт

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

Остаток = Новый Массив;

Для Каждого Элемент Из ИсходныйМассив Цикл

Остаток.Добавить(Элемент);

КонецЦикла;

Результат = Новый Массив;

Пока Остаток.Количество() > 0 Цикл

Индекс = Генератор.СлучайноеЧисло(0, Остаток.ВГраница());

Результат.Добавить(Остаток[Индекс]);

Остаток.Удалить(Индекс);

КонецЦикла;

Возврат Результат;

КонецФункции

Такой вариант удобен, когда исходный порядок нужно сохранить — например, массив пришёл параметром и менять его нельзя по логике вызывающего кода. Но на массивах в десятки тысяч элементов разница в скорости станет заметной, и лучше вернуться к Фишеру–Йетсу.

Сравнение подходов

Фишер–Йетс

Быстрый, равномерное распределение, меняет исходный массив. Подходит для любых объёмов

Случайное извлечение

Проще читается, сохраняет исходный массив, но медленнее на больших коллекциях

Сортировка по случайному ключу

Генерация массива случайных ключей и сортировка по ним. Наглядно, но требует дополнительной структуры данных

Перемешивание таблицы значений и списка значений

В прикладном коде чаще встречаются не «голые» массивы, а таблицы значений и списки значений. Для таблицы значений применим тот же алгоритм с обменом строк, но менять местами нужно содержимое строк целиком — проще всего через промежуточный массив строк или через добавление служебной колонки со случайным числом и последующую сортировку по ней.

// Перемешивание таблицы значений через служебную колонку

ТЗ.Колонки.Добавить("СлучайныйКлюч");

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

Для Каждого Строка Из ТЗ Цикл

Строка.СлучайныйКлюч = Генератор.СлучайноеЧисло(0, 1000000);

КонецЦикла;

ТЗ.Сортировать("СлучайныйКлюч");

ТЗ.Колонки.Удалить("СлучайныйКлюч");

Способ с колонкой-ключом корректен, если диапазон случайных чисел достаточно велик относительно числа строк — иначе совпадения ключей сделают порядок частично предсказуемым (строки с одинаковым ключом сохранят исходный взаимный порядок). Для списка значений удобно выгрузить элементы в массив, перемешать его Фишером–Йетсом и заполнить список заново.

Порядок действий для таблицы значений
  1. 1
    Добавьте служебную числовую колонку
  2. 2
    Заполните её значениями из генератора случайных чисел
  3. 3
    Отсортируйте таблицу по этой колонке
  4. 4
    Удалите служебную колонку
  5. 5
    Проверьте, что число строк и их состав не изменились

Проверка результата и контроль качества перемешивания

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

Для проверки равномерности (например, в тестах общего модуля) можно прогнать перемешивание массива из нескольких элементов тысячи раз и посчитать, как часто каждый элемент оказывается на первой позиции. При корректной реализации частоты будут близки между собой; если один элемент систематически «лидирует», в алгоритме ошибка в диапазонах случайных индексов.

Контрольные точки

Как проверить результат

Количество элементов
Норма

Совпадает с исходным

Тревожный признак

Элементы потеряны или продублированы

Состав элементов
Норма

Отсортированные копии до и после совпадают

Тревожный признак

Появились новые или исчезли старые значения

Порядок
Норма

Меняется от запуска к запуску

Тревожный признак

Порядок каждый раз одинаковый — генератор инициализирован фиксированным зерном

Распределение
Норма

В серии прогонов позиции элементов примерно равновероятны

Тревожный признак

Один элемент стабильно чаще оказывается первым

Типичные ошибки и ограничения платформы

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

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

Типичные ошибки
01
Диапазон СлучайноеЧисло(1, Количество) — выход за границу массива или пропуск первого элемента
02
Создание ГенераторСлучайныхЧисел внутри цикла — повторяющиеся значения из-за близких зерен
03
Выбор случайного индекса по всему массиву на каждом шаге — неравномерное распределение перестановок
04
Обмен элементов без временной переменной — потеря одного из значений
05
Использование фиксированного зерна в прикладной жеребьёвке — предсказуемый результат

Отдельно учитывайте контекст исполнения. Генератор случайных чисел доступен и на сервере, и на клиенте, но в толстом и тонком клиенте поведение одинаково корректно только при соблюдении диапазонов. Если перемешивание выполняется на клиенте над большими данными, разумнее перенести операцию в серверную процедуру — заодно уменьшится трафик.

Выбор подхода под конкретную задачу

Если нужно просто перемешать массив в серверной процедуре — берите Фишера–Йетса без вариантов. Если исходный массив менять нельзя — используйте извлечение в новый массив. Если данные уже в таблице значений и дальше идут в отчёт или СКД — добавьте служебную колонку со случайным ключом и отсортируйте. Если нужна не полная перестановка, а случайная выборка N элементов из M — примените Фишера–Йетса только к первым N позициям, это стандартный приём частичного перемешивания.

Маршрут решения

Как выбрать решение

Нужно перемешать весь массив на сервереАлгоритм Фишера–Йетса с одним генератором случайных чисел
Исходный массив изменять нельзяСлучайное извлечение элементов в новый массив
Данные в таблице значенийСлужебная колонка со случайным ключом и сортировка
Нужна случайная выборка, а не полная перестановкаЧастичный Фишер–Йетс по первым N позициям
Нужен воспроизводимый результат для тестаГенератор с фиксированным зерном — только в тестовом коде

Частые вопросы

Есть ли в 1С готовый метод типа Перемешать()?

Нет. У объекта Массив есть только методы добавления, удаления, поиска, вставки и очистки. Перемешивание всегда реализуется собственной процедурой на базе ГенераторСлучайныхЧисел.

Почему при каждом запуске порядок получается одинаковым?

Скорее всего, генератор создан с фиксированным зерном: Новый ГенераторСлучайныхЧисел(123). Уберите параметр конструктора — тогда последовательность будет различаться между запусками.

Как перемешать строки табличной части документа?

Выгрузите табличную часть в таблицу значений методом Выгрузить(), перемешайте её через служебную колонку со случайным ключом, затем загрузите обратно методом Загрузить(). Учтите, что после этого документ потребуется записать, а изменение порядка строк может повлиять на проведение, если логика зависит от номеров строк.

Работает ли перемешивание на клиенте в управляемых формах?

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

Как получить случайный элемент массива без перемешивания?

Достаточно одного обращения к генератору: Элемент = Массив[Генератор.СлучайноеЧисло(0, Массив.ВГраница())]. Полное перемешивание для этой задачи избыточно.

Можно ли перемешать массив запросом?

Напрямую в языке запросов случайной сортировки нет. Практический обходной путь — выгрузить результат запроса в таблицу значений и перемешать её средствами встроенного языка.

Итог

Что важно запомнить

Готового метода перемешивания в 1С нет — используйте алгоритм Фишера–Йетса с одним генератором случайных чисел, созданным до цикла и без фиксированного зерна. Следите за диапазонами индексов (0..ВГраница), проверяйте сохранность состава массива и не применяйте воспроизводимые последовательности в операциях, влияющих на деньги или распределение благ.