Наши примеры до сих пор были построены на данных и наборах, которые относились к ранее указанным простым наборам, находились на пересечении DEST и ORIG, а ROUTES на пересечении DEST - ORIG - PROD. Эта практика помогает создавать четкие и правильные модели. Тем не менее, если нам неудобно указывать домены как часть данных, мы можем определить их в рамках модели из ранее определенных кортежей. AMPL предоставляет для данной цели итеративный оператор setof:
set ROUTES dimen 3; set PROD = setof {(i,j,p) in ROUTES} p;
Как и итеративный оператор суммирования, за setof следуют индексное выражение {(i,j,p) in ROUTES} и аргумент (p), который может быть любым выражением (“C_” & p), которое оценивается как допустимый элемент набора. Аргумент (p) оценивается для каждого элемента набора индексации. Результаты объединяются в новый набор, который возвращается оператором. Дубликаты участников игнорируются. Таким образом, выражения:
set PROD = setof {(i,j,p) in ROUTES} p; set LINKS = setof {(i,j,p) in ROUTES} (i,j);
для PROD и LINKS дают наборы всех объектов p и пар (i,j), для которых в ROUTES имеется некоторый элемент (i,j,p).
Оператор setof является оператором построения множества. Элементом этого множества является либо выражение (i in ORIG), либо разделенный запятыми список выражений ((i,j,p) in ROUTES}) или ({i in ORIG, j in DEST, p in PROD}), заключенный в скобки. Результирующий набор состоит из всех элементов, полученных результате итерации по индексному выражению. Размерность результирующего выражения - это количество компонентов в аргументе (p) или (i,j).
Общий вид синтаксиса оператора setof
Общий вид синтаксиса выражений при использовании оператора setof имеет следующий вид:
Форма | Описание компонентов |
setof indexing member
Пример: setof {p in PROD} p; |
indexing: индексное выражение
member: аргумент |
С помощью оператора setof можно соединять таблицы данных. В следующем коде происходит объединение 2-х таблиц по одинаковым значениям столбца с.
set P = {1,2,3,4,5}; # Номер книги set G = {"A", "B", "C"}; # Жанр книги set S = {"A10", "A11","A12"}; # Книжная полка set P_G within P cross G; # Привязка номера книги к жанру set P_S within P cross S; # Привязка книги к полке set Handle = setof{(p,g)in P_G, (p,s) in P_S} (g,s); # Какие жанры хранятся на каких полках data; set P_G:= (1,A), (2,A), (3,B), (4,C), (5,B); set P_S:= (1,A11), (2,A10), (3,A12), (4,A11), (5,A10); display Handle;
Следующий пример демонстрирует соединение таблиц для задачи 6.9 AMPL_book:
А при формулировании цели оптимизации происходит увязка таблиц по их индексам:
Сначала мы определяем индексы объединенной таблицы {(r,p) in CanHandle}, затем в таблице Willing мы ищем значение cpref[r,c] соответствующее максимальной компетенции рефери (r) по категории (с) для типа документа (p), определенного ранее в CanHandle (т.к. по условиям задачи документ может относиться к различным категориям, а каждый рефери имеет различный набор оценок для категорий).
Модель выбирает максимальное значение cpref[r,c] из массива {(r,c) in Willing} для которого индекс (r) определен ранее, а индекс (с) выбирается на основании его максимального полезности для рефери и наличием значения по данной категории в таблице (PeperKind) для ранее определенного типа документа (р).
Примеры использования setof:
Динамическое объявление элементов набора
set client = setof {i in 1..9}"C_"&i; # Назначение элементов набора client С_1, С_2...С_9
Уменьшение размерности набора
set LEN:={1,2,3}; set WID:= {4,5,6}; set HEIGHT:= {7,8,9}; set THREE within LEN cross WID cross HEIGHT; set TU= setof{(i,j,k) in THREE}(i,j); # Объявление набора состоящего из уникальных пар (i,j) param x {THREE}; param Y{(i,j) in TU} = sum{k in HEIGHT:(i,j,k) in THREE}x[i,j,k]; data; param: THREE: x:= 1 4 7 4 2 4 7 8; display TU, Y;
Создание элементов набора на основании алфавита
set A_Z = setof {i in 65..90} char(i); display A_Z; # Вывод всех заглавных английских символов set A_IA = setof {i in 93..123}char(i); display A_IA; # Вывод всех заглавных русских символов
Динамическое изменение элементов набора
Иногда, необходимо уменьшить количество элементов набора, который индексирует параметр. В случае реализации прямого удаления элементов набора, вы увидите сообщения об ошибке «invalid subscripts discarded». Этого сообщения невозможно избежать, используя "update data" или "reset data". Вместо этого необходимо определить один параметр для максимального количества данных:
param nscn_max integer > 0, default 1; # Определение максимально возможного количества данных set SCN_MAX = {1..nscn_max}; # объявление набора
Затем нужно определить другой параметр для фактически используемого количества данных:
param nscn integer <= nscn_max; # фактический объем данных < nscn_max set SCN = {1..nscn}; # Набор фактических данных
Используйте SCN_MAX для индексации параметров, но используйте SCN для индексации остальной модели (переменные, цель, ограничения). Например,
param bra_STATUS {SCN_MAX, BRA_ID}; var bus_vm {SCN, BUS_ID};
Теперь можно изменить размер модели, изменив значение nscn. Когда уменьшится размер nscn например с 3 до 1, некоторые данные больше не будут использоваться моделью, но не будут отброшены.