При разработке алгоритмов на 1С часто возникает задача, требующая перебора всех возможных комбинаций элементов из нескольких списков. Математически это называется декартовым произведением. На практике это нужно, например, при генерации всех вариантов комплектаций товаров, сочетаний характеристик или подборе графиков. Давайте разберем, как эффективно решить эту задачу, избегая избыточного использования рекурсии.
Представьте, что у нас есть три массива: в первом варианты 1.1, 1.2, 1.3; во втором — 2.1, 2.2, 2.3; в третьем — 3.1, 3.2. Нам нужно получить полный список всех возможных «кортежей», где первый элемент берется из первого массива, второй — из второго и так далее. Количество вариантов при этом растет экспоненциально, поэтому важно выбрать оптимальный алгоритм.
Вместо использования глубокой рекурсии, которая может быть ресурсозатратной, мы можем воспользоваться методом «счетчика индексов». Суть подхода заключается в поддержании массива текущих индексов, который ведет себя как обычное число в системе счисления, где «разряды» имеют разное основание (длину каждого вложенного массива).
Рассмотрим по шагам, как реализовать этот алгоритм:
Индексы, длина которого совпадает с количеством наших массивов. Изначально заполняем его нулями.Пока собираем текущую комбинацию, обращаясь к элементам массивов по текущим индексам.Рассмотрим подробнее реализацию на 1С. Основной код выглядит следующим образом:
&НаКлиенте
Процедура ВывестиРезультат(Команда)
// Подготовка исходных данных
ВсеМассивы = Новый Массив;
// ... здесь логика заполнения массивов ...
// Инициализация счетчика индексов
Индексы = Новый Массив;
Для Сч = 1 По ВсеМассивы.Количество() Цикл
Индексы.Добавить(0);
КонецЦикла;
Пока Истина Цикл
ВывестиСообщение(ВсеМассивы, Индексы);
Если НЕ УвеличитьИндекс(ВсеМассивы, Индексы) Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Теперь разберем функцию, которая управляет перебором индексов:
&НаКлиенте
Функция УвеличитьИндекс(Массивы, Индексы)
Результат = Ложь;
Для Сч = Индексы.Количество() - 1 По 0 Цикл
Индексы[Сч] = Индексы[Сч] + 1;
Если Индексы[Сч] < Массивы[Сч].Количество() Тогда
Результат = Истина;
Прервать;
Иначе
Индексы[Сч] = 0;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Данный алгоритм позволяет работать с большими объемами данных, так как мы не храним весь результирующий массив в памяти (что могло бы привести к ошибке нехватки памяти при большом количестве комбинаций). Вместо этого мы используем подход «генератора»: получаем одну комбинацию, обрабатываем её и переходим к следующей.
Важные рекомендации:
ВывестиСообщение callback-функцию или выполняйте запись результата сразу в БД или файл.ТипЗнч элементов внутри массивов может быть любым, алгоритм индексов универсален для любого содержимого (ссылки, строки, числа).Таким образом, мы решили задачу формирования декартова произведения, создав универсальный и масштабируемый инструмент для работы с комбинаторными данными в 1С.