В практике программирования на платформе 1С:Предприятие часто возникает задача генерации всех возможных сочетаний наборов данных. Это может быть подбор характеристик товара, формирование графиков смен или перебор вариантов скидок. Математически эта задача называется нахождением декартова произведения множеств или генерацией последовательностей в определенной системе счисления.
Разберем ситуацию, когда у нас есть количество столбцов X и количество возможных значений в каждом столбце Y. Наша цель — получить массив, где каждая строка представляет собой уникальную комбинацию чисел от 0 до Y.
Прежде чем приступать к написанию кода, проанализируем структуру данных. Если нам нужно получить комбинации для 3 столбцов (X=3) с максимальным значением 4 (Y=4), то мы имеем дело с пятиричной системой счисления (так как используются цифры 0, 1, 2, 3, 4). Общее количество комбинаций вычисляется по формуле (Y + 1) ^ X. В нашем примере это 5 ^ 3 = 125 строк.
Рассмотрим последовательность подробнее:
Этот способ является наиболее производительным. Мы запускаем один цикл по общему количеству строк и для каждой итерации вычисляем значения в колонках, используя операции взятия остатка (%) и целочисленного деления.
Рассмотрим пример кода, который универсально решает задачу для любых X и Y:
// X - количество столбцов (разрядов)
// Y - максимальное значение в разряде (от 0 до Y)
Процедура ЗаполнитьМассивКомбинаций(X, Y)
Основание = Y + 1;
ВсегоСтрок = Pow(Основание, X);
// Создаем двухмерный массив: количество строк и количество столбцов
МассивРезультат = Новый Массив(ВсегоСтрок, X);
Для ТекущийИндекс = 0 По ВсегоСтрок - 1 Цикл
ВременноеЧисло = ТекущийИндекс;
// Заполняем разряды с конца к началу (от младшего к старшему)
Для НомерСтолбца = 0 По X - 1 Цикл
// Вычисляем значение в текущем разряде
ЗначениеРазряда = ВременноеЧисло % Основание;
// Записываем в массив (инвертируем индекс столбца, если нужен порядок слева направо)
МассивРезультат[ТекущийИндекс][X - 1 - НомерСтолбца] = ЗначениеРазряда;
// Переходим к следующему разряду
ВременноеЧисло = Цел(ВременноеЧисло / Основание);
КонецЦикла;
КонецЦикла;
// В итоге МассивРезультат содержит все комбинации
КонецПроцедуры
Важный нюанс: В языке 1С при создании массива Новый Массив(Размер1, Размер2) создается массив массивов. Обращение к нему происходит через два индекса: Массив[Строка][Колонка]. Проанализируем эффективность: данный метод требует всего N * X операций, где N — общее количество комбинаций.
Если вы не хотите использовать сложные математические функции (например, Pow или Цел), можно имитировать работу обычного счетчика. Мы создаем одну строку и в цикле "прибавляем единицу" к самому правому разряду. Если значение превышает Y, мы обнуляем его и прибавляем единицу к соседнему левому разряду.
Разберем этот алгоритм по шагам:
Процедура ЗаполнитьМетодомСчетчика(КолСтолбцов, МаксЗначение)
КоличествоВариантов = Pow(МаксЗначение + 1, КолСтолбцов);
Результат = Новый Массив();
// Инициализируем текущую комбинацию нулями
ТекущаяСтрока = Новый Массив(КолСтолбцов);
Для к = 0 По КолСтолбцов - 1 Цикл
ТекущаяСтрока[к] = 0;
КонецЦикла;
Для итер = 1 По КоличествоВариантов Цикл
// Добавляем копию текущей строки в результат
Результат.Добавить(Новый Массив(ТекущаяСтрока));
// Инкремент (прибавление единицы)
Для Позиция = 1 По КолСтолбцов Цикл
Индекс = КолСтолбцов - Позиция;
Если ТекущаяСтрока[Индекс] < МаксЗначение Тогда
ТекущаяСтрока[Индекс] = ТекущаяСтрока[Индекс] + 1;
Прервать; // Выходим из цикла инкремента
Иначе
ТекущаяСтрока[Индекс] = 0; // Сброс разряда и перенос в следующий
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Этот метод интуитивно понятнее и зачастую работает быстрее при очень большом количестве столбцов, так как не выполняет операцию деления в каждой ячейке.
Рекурсия удобна тем, что она естественно описывает вложенность циклов. Если мы заранее не знаем количество столбцов X, мы можем "погружаться" вглубь на каждую новую колонку.
Проанализируем пример рекурсивной функции:
Процедура СформироватьРекурсивно(МассивРезультат, ТекущаяСтрока, НомерКолонки, X, Y)
Если НомерКолонки = X Тогда
// Мы дошли до конца — сохраняем результат
МассивРезультат.Добавить(Новый Массив(ТекущаяСтрока));
Возврат;
КонецЕсли;
Для Значение = 0 По Y Цикл
ТекущаяСтрока[НомерКолонки] = Значение;
СформироватьРекурсивно(МассивРезультат, ТекущаяСтрока, НомерКолонки + 1, X, Y);
КонецЦикла;
КонецПроцедуры
При работе с подобными массивами в 1С следует помнить о нескольких критических моментах:
X=10, а Y=9, то количество строк составит 10 миллиардов. Попытка создать такой массив приведет к немедленному падению сеанса с ошибкой "Недостаточно памяти". Всегда проверяйте теоретическое количество строк перед инициализацией массива.ТаблицаЗначений. Она медленнее при заполнении, но обладает мощным функционалом (индексы, фильтры, агрегатные функции).ТекущаяСтрока в общий список обязательно создавайте копию через Новый Массив(ТекущаяСтрока), иначе все строки в итоговом списке будут ссылаться на одно и то же последнее состояние.Выбор конкретного метода зависит от ваших предпочтений: итерационный метод (Решение 1) наиболее математичен и компактен, в то время как метод счетчика (Решение 2) лучше подходит для оптимизации сложных алгоритмов перебора.