книги / Моделирование и оптимизация в LINGO
..pdfи результатов решения. (Вставить в модель любую функцию можно командой Solver/Paste Function.)
Как отмечалось в начале главы, LINGO имеет 2 утилиты, которые предназначены для сортировки элементов множества или атрибутов. Утилита @RANK(SET|ATTRIBUTE(S)) возвращает вектор, в котором каждому элементу соответствует номер его позиции (ранг) при упорядочении по возрастанию. @SORT(SET|ATTRIBUTE(S)) возвращает вектор, содержащий индексы элементов в порядке, соответствующем отсортированным атрибутам или членам множества.
В качестве примера используем обе утилиты для сортировки значений атрибута:
model: sets:
set1: w, wrank, wsort; endsets
data:
w = 23 9.1 17.5; enddata
calc: @set('terseo',2); wrank= @rank(w);
@writefor(set1(i):'wrank ',(i),' =', @format(wrank, '3.0g'), @NEWLINE( 1));
wsort = @sort(w); @write(@NEWLINE( 1) );
@writefor(set1(i):'wsort ',(i),' =', @format(wsort, '3.0g'), @NEWLINE( 1));
endcalc end
После выполнения этого скрипта получаем
wrank 1 = |
3 |
|
wrank 2 = |
1 |
|
wrank 3 |
= |
2 |
wsort 1 |
= |
2 |
wsort 2 |
= |
3 |
wsort 3 |
= |
1 |
61
7.1. Пример программирования 1
Вернемся к задаче PRODUCTION. Составим модель задачи так, чтобы получить возможность решения ее исходного и линеаризованного вариантов за один запуск решателя. С этой целью выделим в разделе ММ три подмодели, а в секции CALC запишем скрипт, определяющий порядок решения комбинированных моделей и выводимую информацию о полученных решениях. В итоге приходим к следующей модели [14]:
MODEL: |
|
|
|
|
|
Title PRODUCTION; |
|
|
|
||
sets: |
|
|
|
|
|
PRODUCT: order; |
|
|
|
||
EQUIPMENT /1..3/: TWORK, TWORKY,K; |
|
|
|||
PR_EQ (PRODUCT, EQUIPMENT): T, X, Y; |
|
|
|||
endsets |
|
|
|
|
|
data: |
|
|
|
|
|
PRODUCT=pr1.. pr4; |
|
|
|
||
order= 75 123 0 62; |
|
|
|
||
T = 7 |
11 |
9 |
|
|
|
14 |
8 |
10 |
|
|
|
12 |
7 |
13 |
|
|
|
10 |
9 |
11; |
|
|
|
K = 2 1 3; |
|
|
|
|
|
enddata |
|
|
|
|
|
submodel OBJ_1: |
|
|
|
||
MIN = TIME1; |
|
|
|
||
TIME1 = @MAX(EQUIPMENT: TWORK); |
|
|
|||
endsubmodel |
|
|
|
|
|
submodel OBJ_2: |
|
|
|
||
MIN = TIME2; |
|
|
|
||
@FOR(EQUIPMENT: TIME2>=TWORK); |
|
|
|||
endsubmodel |
|
|
|
|
|
submodel CONSTR: |
|
|
|
||
@FOR(PRODUCT(I)|order #NE# 0: @SUM(EQUIPMENT(J): |
|
||||
X(I,J))=order); |
@SUM(PRODUCT(I)|order |
#NE# |
0: |
||
@FOR(EQUIPMENT(J): |
T(I,J)*X(I,J))- TWORK(J)=0); @FOR(PR_EQ(I,J)|order(I) #NE# 0: @GIN(X)); @FOR(EQUIPMENT(J): @CARD( 'name'+ EQUIPMENT(J), K);
@FOR(PRODUCT(I)|order #NE# 0: @CARD('name'+ EQUIPMENT(J), X(I,J))));
62
endsubmodel calc:
@SET('TERSEO',1);
@WRITE(11*' ','РЕШЕНИЕ ЛИНЕАРИЗОВАННОЙ МОДЕЛИ', @NEWLINE(1));
@WRITE(11*' ',30*'-',@NEWLINE(1)); @SOLVE(OBJ_2, CONSTR);
@FOR(PR_EQ(i,j):Y(i,j)=X(i,j);TWORKY(J)=TWORK(J)); @WRITE(' Переменные линеаризованной модели',
@NEWLINE(1));
@TABLE(Y); @WRITE(@NEWLINE(2));
@WRITE(11*' ','РЕШЕНИЕ ИСХОДНОЙ МОДЕЛИ',@NEWLINE(1)); @WRITE(11*' ',23*'-',@NEWLINE(1));
@SOLVE(OBJ_1, CONSTR);
@WRITE(' Переменные исходной модели',@NEWLINE(1)); @TABLE(X);@WRITE(@NEWLINE(2));
@CHARTBAR(
'PRODUCTION', !Chart title;
'PRODUCT by EQUIPMENT', !X-Axis label; 'PRODUCT QUANTITY', !Y-Axis label; 'OBJ_2', !Legend 1;
Y, !Attribute 1; 'OBJ_1', !Legend 2; X);
@CHARTBAR(
'PRODUCTION', !Chart title; 'EQUIPMENT', !X-Axis label; 'TIME of WORK', !Y-Axis label; 'OBJ_2', !Legend 1;
TWORKY, !Attribute 1; 'OBJ_1', !Legend 2; TWORK);
endcalc END
Здесь установлен режим краткого отчета с выводом переменных в виде таблиц и диаграмм. Первой решается линеаризованная модель, образованная двумя подмоделями. Затем решается исходная модель, составленная также из двух подмоделей. Вывод отчетов в двух вариантах (таблица и диаграмма) призван проиллюстрировать работу операторов программирования.
В результате решения этой модели получаем единый отчет по двум комбинированным моделям:
63
|
РЕШЕНИЕ ЛИНЕАРИЗОВАННОЙ МОДЕЛИ |
||
|
------------------------------ |
||
Global optimal solution found. |
745.0000 |
||
Objective value: |
|
||
Objective bound: |
|
745.0000 |
|
Infeasibilities: |
|
0.000000 |
|
Extended solver steps: |
0 |
||
Total solver iterations: |
30 |
||
Elapsed runtime seconds: |
0.06 |
||
Переменные линеаризованной модели |
|||
PR1 |
1 |
2 |
3 |
75.00000 |
0.000000 |
0.000000 |
|
PR2 |
0.000000 |
93.00000 |
30.00000 |
PR3 |
0.000000 |
0.000000 |
0.000000 |
PR4 |
22.00000 |
0.000000 |
40.00000 |
|
РЕШЕНИЕ ИСХОДНОЙ МОДЕЛИ |
|
|
|
----------------------- |
|
|
Local optimal solution found. |
795.0000 |
||
Objective value: |
|
||
Objective bound: |
|
795.0000 |
|
Infeasibilities: |
|
0.000000 |
|
Extended solver steps: |
10 |
||
Total solver iterations: |
1802 |
||
Elapsed runtime seconds: |
1.84 |
||
Переменные исходной модели |
3 |
||
PR1 |
1 |
2 |
|
0.000000 |
0.000000 |
75.00000 |
|
PR2 |
12.00000 |
99.00000 |
12.00000 |
PR3 |
0.000000 |
0.000000 |
0.000000 |
PR4 |
62.00000 |
0.000000 |
0.000000 |
Как и ранее, на линеаризованной модели находится глобальный минимум, а на исходной (нелинейной) модели – только локальный, но отличающийся от найденного локального минимума при первом решении этой модели. Таким образом, исходная модель является многоэкстремальной, причем локальные минимумы значительно отличаются от глобального.
Наглядное представление о различиях в решениях двух моделей дают диаграмма выпуска продукции (рис. 2) и диаграмма времени работы оборудования (рис. 3).
64
Рис. 2
Рис. 3
Приведенный пример демонстрирует, с одной стороны, возможности языка моделирования LINGO, а с другой – важность преобразования модели в модель более простого класса, в идеале – в линейную модель, гарантирующую нахождение глобального решения.
65
7.2.Пример программирования 2
Вэтом примере рассмотрим решение одной из известных задач раскроя, встречающейся при производстве бумаги, картона, металла, текстиля и т.п.
Сначала сформулируем проблему, которую будем решать. Задача состоит в том, чтобы найти такие карты раскроя двумерного материала, при использовании которых расход материала на выполнение всего заказа был бы минимальным. В качестве исходного материала возьмем рулоны определенной ширины. Из них требуется получить заказанное количество рулонов разной ширины, которые будем называть лентами, чтобы отличать их от раскраиваемых рулонов. Так как возможны разные карты раскроя рулона, а их может быть слишком много, решение задачи заключается в нахождении самого эффективного набора таких карт. Очевидно, что реальную задачу не решить полным перебором всех карт раскроя, обеспечивающих выполнение заказа.
В1961 г. П. Гилмори и Р. Гомори предложили эффективный двухэтапный алгоритм решения задачи раскроя, в частности рулонов, значительно сокращающий число рассматриваемых карт раскроя [16].
На первом этапе решается основная модель задачи на множестве карт, сгенерированных к началу текущей итерации, с условием удовлетворения всех потребностей минимумом используемых рулонов. На начальной итерации множество карт состоит из одной искусственной карты, содержащей всю номенклатуру лент (авторы назвали ее суперкартой). Результатом первого этапа является найденный на текущем множестве очередной оптимальный набор карт
ичастота их применения, а также двойственные цены лент. При этом на всех итерациях за исключением завершающей итерации решается непрерывная линейная модель.
На втором этапе используются найденные двойственные цены в качестве цен (Price) лент, характеризующие их значимость, для нахождения самой лучшей новой карты по суммарной цене лент, входящих в нее. Поэтому данный этап называют генератором карт или столбцов симплекс-метода (новая карта вводит новую перемен-
66
ную и соответствующий столбец в симплекс-таблице). Задача поиска максимума значимости набора лент, ограниченного шириной рулона, представляет собой известную классическую задачу о рюкзаке с одним условием (ширина рулона – это емкость рюкзака). При получении перспективной карты она добавляется в текущий набор карт, и снова решается основная модель.
Если не удается найти такую новую карту, то основная модель решается совместно с подмоделью целочисленности переменных, т.е. как целочисленная линейная задача. Решается она методом ветвей и границ.
Полная модель задачи раскроя рулонов имеет следующий вид:
MODEL:
! ЗАДАЧА РАСКРОЯ РУЛОНОВ;
SETS:
PATTERN: COST, X;
FG: WIDTH, DEM, PRICE, Y, YIELD; FXP( FG, PATTERN): NBR;
ENDSETS
DATA:
PATTERN = 1..15; ! Полагаем, что не потребуется; !больше 15 карт;
RMWIDTH = 8; ! Ширина раскраиваемого рулона, м; FG = L1 L2 L3 L4 L5; !Требуемые рулоны (ленты);
WIDTH= 6 4 2.5 1.2 0.8; !их ширина;
DEM = 250 110 700 920 535; !и потребность; BIGM = 999;
ENDDATA
! Основная модель;
SUBMODEL MASTER_PROB:
[MSTROBJ] MIN= @SUM( PATTERN( J)| J #LE# NPATS: COST( J)*X( J));
@FOR( FG( I): [R_DEM]
@SUM( PATTERN( J)| J #LE# NPATS:
NBR( I, J) * X( J)) >= DEM( I);
); ENDSUBMODEL
! Подмодель целочисленности;
SUBMODEL INTEGER_REQ:
@FOR( PATTERN: @GIN( X)); ENDSUBMODEL
67
! Подмодель генерации карт;
SUBMODEL PATTERN_GEN:
[SUBOBJ] MAX = @SUM( FG( I): PRICE( I)* Y( I)); @SUM( FG( I): WIDTH( I)*Y( I)) <= RMWIDTH; @FOR( FG( I): @GIN(Y( I)));
ENDSUBMODEL
! Скрипт процесса решения задачи; CALC:
! Установка параметров; @SET( 'DEFAULT');
@SET( 'TERSEO', 2); ! Запрет вывода по умолчанию;
!Максимально допустимое число карт; MXPATS = @SIZE( PATTERN);
!Создание 1-й карты как суперкарты; COST( 1) = BIGM;
@FOR( FG( I): NBR( I, 1) = 1);
!Выполнение цикла пока есть перспективные карты; NPATS = 1;
RC = -1; ! Вход в цикл;
@WHILE( RC #LT# 0 #AND# NPATS #LT# MXPATS:
!Решение основной модели с лучшими картами; @SOLVE( MASTER_PROB);
!Сохранение двойственных оценок лент для;
!подмодели генерирования карт;
@FOR( FG( I): PRICE( I) = -@DUAL( R_DEM( I)));
!Генерирование перспективной карты; @SOLVE( PATTERN_GEN);
!Относительная оценка лучшей карты на текущей итерации; RC = 1 – SUBOBJ;
!Добавление карты в главную модель,;
!если она улучшает решение;
@IFC( RC #LT# 0: NPATS = NPATS + 1;
@FOR( FG( I): NBR( I, NPATS) = Y( I)); COST( NPATS) = 1;
);
);
! Решение основной модели с условием целочисленности; @SOLVE( MASTER_PROB, INTEGER_REQ);
ENDCALC
68
!Скрипт вычисления итогов и вывода решения;
!в табличной форме;
CALC:
! Вычисление полученных лент (факт); @FOR( FG( F): YIELD( F) =
@SUM( PATTERN( J)| J #LE# NPATS: NBR( F, J) * X(J))
);
! Вычисление итоговых показателей раскроя; @WRITE(24*' ','ЗАДАЧА РАСКРОЯ РУЛОНОВ ', @NEWLINE( 1));
TOTAL_FT_USED = @SUM( PATTERN( I) | I #LE# NPATS: X( I)) * RMWIDTH;
TOTAL_FT_YIELD = @SUM(FG(I) | I #LE# NPATS: YIELD(I) * WIDTH( I));
PERC_WASTE = 100 * ( 1 – (TOTAL_FT_YIELD / TOTAL_FT_USED));
! Вывод результатов решения на экран; FW = 6;
@WRITE( @NEWLINE(1));
@WRITE(' Всего использовано рулонов: ', @SUM(PATTERN(I)| I #LE# NPATS: X( I)), @NEWLINE(2),
' Совокупная ширина рулонов, м: ', TOTAL_FT_USED, @NEWLINE(1),
' Совокупная ширина лент, м: ', TOTAL_FT_YIELD, @NEWLINE(2),
'Всего остатков, %:', @FORMAT( PERC_WASTE,
'#5.2G'), @NEWLINE(1));
@WRITE( @NEWLINE(1), 24*' ', 'Карты раскроя (Pattern):', @NEWLINE(1));
@WRITE( ' Ленты Заказ Факт');
@FOR( PATTERN(I) | I #LE# NPATS: @WRITE(@FORMAT(I,'6.6G'))); @WRITE( @NEWLINE(1));
@WRITE( ' ',FW*( NPATS+3)*'=', @NEWLINE(1)); @FOR( FG( F):
@WRITE((FW – @STRLEN( FG( F)))*' ', FG( F), ' ', @FORMAT( DEM( F), '6.6G'), @FORMAT( YIELD( F), '6.6G'));
@FOR( FXP( F, P) | P #LE# NPATS: @WRITE( @IF( NBR( F, P) #GT# 0, @FORMAT( NBR( F, P), "6.6G"), ' .')));
@WRITE(@NEWLINE(1))
);
@WRITE( ' ',FW*( NPATS+3)*'=', @NEWLINE(1)); @WRITE( ' ', 'Используется, раз:');
@WRITEFOR(PATTERN(P) | P#LE# NPATS: @FORMAT( X(P), '6.6G')); @WRITE(@NEWLINE(1));
ENDCALC END
69
В этой модели первичное множество PATTERN состоит из карт раскроя с атрибутами стоимость (COST) и количество применений X (переменные основной модели MASTER_PROB). Второе первичное множество (FG) включает требуемый набор готовой продукции (лент). Атрибутами множества являются ширина WIDTH, потребность DEM, цена PRICE, количество лент в карте Y (переменные подмодели генерирования карт PATTERN_GEN) и суммарное произведенное количество лент YIELD. Производное множество FXP является плотным на наборе сгенерированных карт и имеет один атрибут NBR – число лент в карте, определяемое для новой карты как Y.
Раздел математической модели включает в себя три подмодели: MASTER_PROB – основная модель, минимизирующая число раскроенных рулонов; PATTERN_GEN – модель генерирования карт или модель задачи о рюкзаке, определяющая текущий наилучший вариант раскроя рулона (новую карту); INTEGER_REQ – модель, устанавливающая целочисленность переменных X.
Двухэтапный процесс решения задачи реализуется скриптом первой секции CALC. Остановимся здесь на критерии, по которому определяется перспективность или бесполезность добавления новой карты. Им является относительная оценка карты RC (Reduce Cost). Формула ее вычисления исходит из того, что все коэффициенты целевой функции (COST) основной модели за исключением первого равны 1. Первая карта, содержащая по одной ленте каждого размера, является искусственной (суперкартой) и в реальный набор карт попасть не должна. С этой целью ей придается очень большой коэффициент (BIGM). Оценка реальной карты согласно симплекс-методу определяется как разность маргинальных значений ухудшения
иулучшения основного критерия, здесь они соответственно равны 1
иSUBOBJ. Решение основной задачи будет оптимальным, если не будет отрицательных оценок. Поэтому цикл WHILE будет выполняться, пока есть отрицательные оценки и число карт не достигнет заданного предела. Так как оценка определяется в результате решения задачи о рюкзаке, она является минимально возможной и равен-
70