Переменные
Переменная — неизвестная величина, значение которой определяется решателем. Переменные служат базовыми элементами модели; выражения, ограничения и целевая функция формулируются над ними.
Все переменные модели ограничений целочисленные. Если значения прикладной задачи представлены вещественными числами, они масштабируются до целочисленного диапазона на этапе подготовки данных (стоимость в копейках вместо рублей, длительность в минутах вместо часов).
Способы создания переменных
Любая переменная модели определена на домене — подмножестве целых чисел, из которого решатель выбирает её значение. Различия между методами регистрации сводятся к тому, как этот домен задаётся при создании переменной:
Переменные().Добавить— домен не указывается явно: переменная принимает значения из широкого подмножестваInt64. Применяется, когда границы заранее неизвестны и выводятся из ограничений.Переменные().ДобавитьИзДиапазона— домен задаётся сплошным интервалом[a, b]. Самый частый способ: множество значений по смыслу укладывается в один диапазон (день недели, индекс смены, номер станка, объём продукции).Переменные().ДобавитьИзДомена— домен передаётся явно произвольной структуры: объединение диапазонов, перечисление значений, дополнение и т.п. Применяется, когда допустимое множество нерегулярное и не сводится к одному диапазону. Способы построения домена описаны в разделе Домены.Переменные().ДобавитьБулеву— домен фиксирован как[0, 1]. Сокращение для частного случая переменной диапазона; помимо обычной целочисленной семантики такая переменная играет особую роль индикатора (см. ниже).Переменные().Константа— домен состоит из одного значения. Удобна как именованное число в ограничениях и выражениях.
Все методы возвращают одинаковую по структуре фиксированную структуру; различается только домен, с которым переменная попадает в модель. Возвращённая структура сохраняется в локальной переменной 1С, передаётся в другие методы и хранится в коллекциях прикладного кода.
Имена переменных
Каждой переменной присваивается имя. Если имя не указано пользователем, оно генерируется автоматически по шаблону _<индекс> (например, _0, _1, _2).
Имя используется в двух сценариях:
- обращение к значению переменной в решении по имени — без удержания ссылки на объект переменной;
- запись имени в строковом линейном выражении — например,
"2*x + y - 5".
Если переменные впоследствии читаются по имени, осмысленные имена назначаются явно (например, "расход_материала"). При массовой регистрации переменных в цикле автогенерируемых имён обычно достаточно.
Булева переменная как индикатор
Помимо роли обычной целочисленной переменной с диапазоном [0, 1], булева переменная связывает между собой различные части модели:
- условие активации ограничения — ограничение действует только при истинности булевой переменной (см. Ограничения — обязательные и условные);
- флаг существования интервала — опциональный интервал учитывается в расписании только при истинности связанной булевой переменной (см. Интервалы и ресурсы);
- признак выбора альтернативы — ограничения над массивом булевых переменных (
ТолькоОдин,НиМенееОдного) задают логику выбора (см. Булева логика); - индикатор использования дуги графа — булева переменная отмечает, входит ли дуга в найденный маршрут (см. Графы и автоматы).
Подробное описание приёма «индикатор и его отрицание для дизъюнкции ограничений» приведено в разделе Ограничения — индикаторные булевы переменные.
Из булевых переменных строятся производные булевы выражения без введения новых переменных:
- отрицание (
Модель.Переменные().Отрицание(Б)) — соответствуетНЕ Б; - константы
Переменные().ЗначениеИстина()иПеременные().ЗначениеЛожь()— фиксированные истина и ложь, применяются как нейтральные условия в обобщённом коде.
Производные булевы выражения принимаются в качестве условий ограничений наравне с булевыми переменными.
Массивы переменных
В большинстве прикладных задач переменные нужны массивами: булева переменная для каждой пары работник × задача, целочисленная переменная для каждого дня недели, переменная диапазона для каждой операции производственного плана. Поэлементная регистрация заменяется фабриками массивов:
Переменные().ДобавитьМассив— массив полных переменных;Переменные().ДобавитьМассивИзДиапазона— массив переменных с одинаковым диапазоном;Переменные().ДобавитьМассивИзДомена— массив переменных с общим доменом;Переменные().ДобавитьМассивБулевых— массив булевых переменных.
Все фабрики принимают префикс имени и формируют имена вида <префикс>0, <префикс>1, ... — это позволяет ссылаться на конкретные переменные в строковых выражениях.
Метод Переменные().ДобавитьСловарь связывает целочисленную переменную, принимающую значения 0..N-1, с массивом из N булевых переменных.
Эквивалентность двусторонняя: если целочисленная переменная равна i, то i-я булева переменная истинна, остальные ложны.
Словарь применяется в задачах, в которых часть ограничений естественнее формулируется над целым числом, а часть — над набором булевых индикаторов выбора.
Типичный сценарий — кодирование перечислений и классификаторов: дней недели (Пн, Вт, Ср, ...), смен (Утро, День, Ночь), разрядов работников (Первый, Второй, Третий), категорий клиентов и т.п. Целочисленная переменная удобна для сквозных ограничений по конкретному значению (например, «день обучения должен совпадать у всей бригады»), а булевы индикаторы — для подсчёта и условной активации других ограничений (например, «не более двух ночных смен подряд» или «если работник второго разряда — доплата за наставничество»).
Например, при планировании дежурств для каждого работника заводится словарь по дням недели:
ДниНедели = 7; // 0 — понедельник, ..., 6 — воскресенье
Для Каждого Работник Из СписокРаботников Цикл
День = Модель.Переменные().ДобавитьИзДиапазона(
0,
ДниНедели - 1,
СтрШаблон("деньДежурства_%1", Работник.Код)
);
Индикаторы = Модель.Переменные().ДобавитьМассивБулевых(
ДниНедели,
СтрШаблон("дежурит_%1_день", Работник.Код)
);
Модель.Переменные().ДобавитьСловарь(День, Индикаторы);
ДеньДежурства [Работник] = День; // удобно для ограничений «у напарников день дежурства совпадает»
ИндикаторыДней [Работник] = Индикаторы; // удобно для ограничений «в субботу дежурят минимум двое»
КонецЦикла;
Та же пара представлений позволяет одновременно сформулировать «у напарников день дежурства совпадает» (через равенство целых переменных) и «в каждый день недели должно быть назначено не менее одного дежурного» (через сумму булевых индикаторов соответствующего дня по всем работникам).
Иллюстрация: переменные для задачи назначения
В задачах задач назначения применяется матрица булевых переменных «работник × задача», в которой значение 1 означает, что работник назначен на задачу:
КоличествоРаботников = СписокРаботников.Количество();
КоличествоЗадач = СписокЗадач.Количество();
Назначения = О2.Утилиты().МассивИзвестногоРазмера(КоличествоРаботников);
Для Р = 0 По КоличествоРаботников - 1 Цикл
Назначения[Р] = Модель.Переменные().ДобавитьМассивБулевых(
КоличествоЗадач,
СтрШаблон("назн_%1_", Р)
);
КонецЦикла;
На построенную матрицу впоследствии накладываются ограничения «каждой задаче ровно один исполнитель», «нагрузка работника не превышает лимита» и аналогичные. Полностью задача с балансировкой нагрузки разобрана на странице Назначение исполнителей с балансировкой нагрузки.
Полный перечень методов
Полный набор методов регистрации переменных и работы с булевыми выражениями приведён в разделе Программный интерфейс — Переменные.