1. Завдання
Розв’язати багатокритеріальну задачу лінійного програмування з отриманням компромісного розв’язку за допомогою теоретико-ігрового підходу.
Задача (варіант 1):
Z
1
=
x
1
+2
x
2
+
x
3
®
max
Z2
= – x1
–2x2
+x3
+x4
®
min
Z3
= –2x1
–x2
+x3
+x4
®
max
з обмеженнями
2
x
1
–
x
2
+3
x
3
+4
x
4
£
10
x
1
+
x
2
+
x
3
–
x
4
£
5
x
1
+2
x
2
–2
x
3
+4
x
4
£
12
"
x
³
0
2. Теоретичні відомості
У цій роботі реалізовано вирішування таких задач лінійного програмування: розв’язування задач багатокритеріальної оптимізації, тобто пошук компромісного рішення для задач з кількома функціями мети.
Ця задача така:
Задано об’єкт управління, що має n
входів і k
виходів. Вхідні параметри складають вектор X = {xj
},
. Кожен з вхідних параметрів може мати обмеження, що накладене на область його значень. В програмі підтримуються параметри без обмежень на значення, і з обмеженнями невід’ємності (з областю
). Також на комбінації вхідних значень можуть бути накладені обмеження як система лінійних рівнянь або нерівностей:
Вихідні сигнали об’єкта є лінійними комбінаціями вхідних сигналів. Для досягнення ефективності роботи об’єкта управління частину вихідних сигналів треба максимізувати, інші – мінімізувати, змінюючи вхідні сигнали і дотримуючись обмежень на ці сигнали (задоволення усіх нерівностей, рівнянь і обмежень області значень кожного з вхідних параметрів). Тобто вихідні сигнали є функціями мети від вхідних:
Як правило, для багатокритеріальної задачі не існує розв’язку, який би був найкращим (оптимальним) для усіх функцій мети одночасно. Проте можна підібрати такий розв’язок, який є компромісним для усіх функцій мети (в точці цього розв’язку кожна з функцій мети якнайменше відхиляється від свого оптимального значення в заданій системі умов (обмежень).
Тут реалізовано пошук компромісного розв’язку за допомогою теоретико-ігрового підходу, що був розроблений під керівництвом доцента ХАІ Яловкіна Б.Д. Цей підхід дозволяє знайти компромісний розв’язок з мінімальним сумарним відхиленням всіх виходів (значень функцій мети) від їхніх екстремальних значень за даної системи обмежень.
Йде пошук компромісного вектора значень змінних в такому вигляді:
тут
– вектор, що оптимальний для i
-го критерію(функції мети); l
i
– вагові коефіцієнти.
Для отримання цього вектора виконуються такі кроки розв’язування:
1) Розв’язується k
однокритеріальних задач ЛП за допомогою симплекс-методу (для кожної з функцій мети окремо, з тією самою системою обмежень, що задана для багатокритеріальної задачі). Так отримуємо k
оптимальних векторів значень змінних (для кожної з цільових функцій – свій).
2) Підраховуються міри неоптимальності для всіх можливих підстановок кожного вектора значень змінних у кожну з функцій мети, за такою формулою:
де Cj
– вектор коефіцієнтів j
-ої функції мети;
X*
i
– вектор, що оптимальний для i
-
ої функції мети;
X*
j
– вектор, що оптимальний для j
-
ої функції мети;
Всі ці міри неоптимальності складають квадратну матрицю, рядки якої відповідають k
оптимальним векторам X*
i
для кожної функції мети, а стовпці – k
функціям мети Cj
. Ця матриця розглядається як платіжна матриця матричної гри двох партнерів X*
і Z
, що визначена множиною стратегій X*={X*1
, …, X*k
}
першого гравця, і Z={C1
X, …, Ck
X}
другого. Всі міри неоптимальності є недодатними, і є коефіцієнтами програшу першого гравця. На головній діагоналі вони рівні нулю (бо є мірами неоптимальності оптимального вектора для своєї ж функції).
3) Матриця мір неоптимальності заміняється еквівалентною їй матрицею додаванням до кожної міри неоптимальності
, тобто найбільшого з абсолютних значень всіх мір. Якщо таке найбільше значення рівне нулю, то всі міри рівні нулю, і в такому випадку замість нього до усіх мір додається число 1. В результаті отримуємо матрицю з невід’ємними елементами. На головній діагоналі усі вони рівні максимальному значенню. Така заміна матриці не змінює рішення гри, змінює тільки її ціна. Тобто тепер гра має вигляд не гри програшів, а гри з пошуком максимального виграшу. Для пошуку оптимальної стратегії для першого гравця гра подається як пара взаємнодвоїстих однокритеріальних задач ЛП. Для першого гравця потрібні значення змінних двоїстої задачі
:
v1
=
|
v2
=
|
…
|
vk
=
|
W=
|
-
|
-
|
…
|
-
|
1
|
-u1
|
=
|
|
|
…
|
|
1
|
-u2
|
=
|
|
|
…
|
|
1
|
…
|
…
|
.
|
.
|
.
|
.
|
.
|
-uk
|
=
|
|
|
…
|
|
1
|
1
|
Z
= |
-1
|
-1
|
…
|
-1
|
0
|
Розв’язавши цю задачу і отримавши оптимальні значення max(Z) = min(W)
, що досягаються при значеннях змінних двоїстої задачі
, можна обчислити вагові коефіцієнти
для компромісного розв’язку багатокритеріальної задачі:
,
Компромісний вектор значень змінних для багатокритеріальної задачі є лінійною комбінацією оптимальних векторів кожної функції мети. Це сума векторів, що помножені кожен на свій ваговий коефіцієнт:
Підставивши цей компромісний вектор в кожну функцію мети багатокритеріальної задачі отримуємо компромісні значення цих функцій.
3. Вирішування
Рівняння, нерівності та функції записуються у таблицю:
Розв’язування задачі ЛП для кожної функції мети окремо:
Пошук оптимального розв’язку для функції Z1
Задача для симплекс-метода з функцією Z1
Незалежних змінних немає.
Виключення 0-рядків: немає.
Опорний розв’язок: готовий (усі вільні члени невід’ємні).
Пошук оптимального розв’язку:
Результат для прямої задачі:
У рядку-заголовку:
– x1 = 0;
– y2 = 0;
– y1 = 0;
– y3 = 0;
У стовпці-заголовку:
x3 = 2,33333333333333;
x2 = 4,55555555555556;
x4 = 1,88888888888889;
Функція мети: Z1 = 11,4444444444444.
Пошук оптимального розв’язку для функції Z2
Функцію
Z
2, що мінімізується, замінили на протилежну їй –
Z
2, що максимізується. Запис для вирішування симплекс-методом максимізації
Незалежних змінних немає.
0-рядків немає.
Опорний розв’язок: готовий.
Пошук оптимального:
Після отримання розв’язку максимізації для
–
Z
2
, взято протилежну до неї функцію
Z
2
, і отримано розв’язок мінімізації для неї
Результат для прямої задачі:
У рядку-заголовку:
– x1 = 0;
– y2 = 0;
– x3 = 0;
– y3 = 0;
У стовпці-заголовку:
y1 = 14;
x2 = 5,33333333333333;
x4 = 0,333333333333333;
Функція мети: Z2 = -10,3333333333333.
Пошук оптимального розв’язку для функції Z3
Задача для симплекс-методу максимізації
Незалежних змінних і 0-рядків немає.
Опорний розв’язок вже готовий.
Пошук оптимального:
Результат для прямої задачі:
У рядку-заголовку:
– x1 = 0;
– x2 = 0;
– y1 = 0;
– x4 = 0;
У стовпці-заголовку:
x3 = 3,33333333333333;
y2 = 1,66666666666667;
y3 = 18,6666666666667;
Функція мети: Z3 = 3,33333333333333.
Підрахунок мір неоптимальності
Матриця мір неоптимальності та рядок функції мети, стовпець вільних членів і заголовки задачі ЛП, що будуть використані далі
До мір додана найбільша за модулем міра
. Матриця у формі задачі ЛП
Розв’язування ігрової задачі:
Незалежних змінних немає.
0-рядків немає.
Опорний розв’язок вже готовий.
Пошук оптимального розв’язку:
Результат для двоїстої задачі (відносно розв'язаної):
У рядку-заголовку:
u1 = 0,402684563758389;
u3 = 0,174496644295302;
v1 = 0,319280641167655;
У стовпці-заголовку:
– v3 = 0;
– v2 = 0;
– u2 = 0;
Функція мети: Z = 0,577181208053691.
############
Вагові коефіцієнти (Li[Func]=ui/W(U)):
l[Z1] = 0,697674418604651
l[Z2] = 0
l[Z3] = 0,302325581395349
Компромісні значення змінних
x1 = 0
x2 = 3,17829457364341
x3 = 2,63565891472868
x4 = 1,31782945736434
Компромісні значення функцій мети:
Z1 = 8,9922480620155
Z2 = -2,4031007751938
Z3 = 0,775193798449612
Вирішування закінчено. Успішно.
4. Текст програми
Модуль опису класу, що виконує роботу з задачами ЛП:
unit UnMMDOpr;
interface
Uses SysUtils, Types, Classes, Forms, Controls, StdCtrls, Dialogs, Graphics,
Grids, UControlsSizes, Menus;
Const sc_CrLf=Chr(13)+Chr(10);
sc_Minus='-';
sc_Plus='+';
sc_Equal='=';
sc_NotEqual='<>';
sc_Mul='*';
sc_Space=' ';
sc_KrKm=';';
sc_BrOp=' ('; sc_BrCl=')';
sc_XVarName='x';
sc_YFuncName='y';
sc_DualTaskFuncNameStart='v';
sc_DualTaskVarNameStart='u';
sc_RightSideValsHdr='1';
sc_DestFuncHdr='Z';
sc_DualDestFuncHdr='W';
sc_TriSpot='…'; sc_Spot='.';
sc_DoubleSpot=':';
sc_DoubleQuot='"';
lwc_DependentColor:TColor=$02804000;
lwc_IndependentColor:TColor=$02FF8000;
lwc_RightSideColColor:TColor=$02FFD7AE;
lwc_HeadColColor:TColor=$02808040;
lwc_FuncRowColor:TColor=$02C080FF;
lwc_DestFuncToMaxNameColor:TColor=$024049FF;
lwc_DestFuncToMinNameColor:TColor=$02FF4940;
lwc_DestFuncValColor:TColor=$02A346FF;
lwc_ValInHeadColOrRowColor:TColor=$025A5A5A;
lwc_SolveColColor:TColor=$02AAFFFF;
lwc_SolveRowColor:TColor=$02AAFFFF;
lwc_SolveCellColor:TColor=$0200FFFF;
bc_FixedRows=2; bc_FixedCols=1;
{Кількість стовпців перед стовпцями змінних та після них,
які можна редагувати, для редагування таблиці задачі
лінійного програмування (максимізації чи мінімізації функції):}
bc_LTaskColsBeforeVars=1; bc_LTaskColsAfterVars=1;
bc_LTaskRowsBeforeVars=bc_LTaskColsBeforeVars;
bc_LineEqM1ColsBeforeVars=1;
bc_LineEqM2ColsAfterVars=1;
bc_NotColored=-1;
bc_Negative=-1; bc_Zero=0; bc_Positive=1;
bc_MenuItemColorCircleDiameter=10;
sc_DependentVar='Залежна змінна (>=0)';
sc_IndependentVar='Незалежна змінна (будь-яке дійсне число)';
sc_FreeMembers='Вільні члени (праві сторони рівнянь)';
sc_InequalFuncName='Назва функції умови-нерівності';
sc_DestFuncCoefs='Рядок коефіцієнтів функції мети';
sc_DestFuncName='Назва функції мети';
sc_DestFuncToMaxName=sc_DestFuncName+', що максимізується';
sc_DestFuncToMinName=sc_DestFuncName+', що мінімізується';
sc_OtherType='Інший тип';
sc_DestFuncVal='Значення функції мети';
sc_ValInHeadColOrRow='Число у заголовку таблиці';
sc_SolveCol='Розв''язувальний стовпець';
sc_SolveRow='Розв''язувальний рядок';
sc_SolveCell='Розв''язувальна комірка';
Type
TWorkFloat=Extended; {тип дійсних чисел, що використовуються}
TSignVal=-1..1;
{Ідентифікатор для типу елемента масиву чисел та імен змінних.
Типи змінних: залежні, незалежні, функції (умови-нерівності).
Залежні змінні – це змінні, для яких діє умова невід'ємності:}
THeadLineElmType=(bc_IndependentVar, bc_DependentVar, bc_FuncVal, bc_Number,
bc_DestFuncToMax, bc_DestFuncToMin, bc_OtherType);
THeadLineElmTypes=set of THeadLineElmType;
TVarNameStr=String[7]; {короткий рядок для імені змінної}
TValOrName=record {Елемент-число або назва змінної:}
ElmType:THeadLineElmType;
Case byte of
1: (AsNumber:TWorkFloat); {для запису числа}
2: (AsVarName:TVarNameStr; {для запису назви змінної}
{Для запису номера змінної по порядку в умові задачі (в рядку
чи стовпці-заголовку):}
VarInitPos: Integer;
{Відмітка про те, що змінна була у рядку-заголовку (
True
), або
у стовпцю-заголовку (
False
):}
VarInitInRow: Boolean);
End;
TValOrNameMas=arrayofTValOrName; {тип масиву для заголовків матриці}
TFloatArr=arrayofTWorkFloat; {тип масиву дійсних чисел}
TFloatMatrix=array of TFloatArr; {тип матриці чисел}
TByteArr=array of Byte; {масив байтів – для поміток для змінних}
TByteMatrix=array of TByteArr;
{Стани об'єкта форматування таблиці у GrowingStringGrid:}
TTableFormatState=(fs_EnteringEqs, fs_EnteringLTask, fs_SolvingEqsM1,
fs_SolvingEqsM2, fs_SolvingLTask,
fs_NoFormatting, fs_FreeEdit);
{Тип переходу до двоїстої задачі: від задачі максимізації до
задачі мінімізації, або навпаки. Ці два переходи виконуються за
різними правилами (різні правила зміни знаків «<=» та «>=»
при переході від нерівностей до залежних змінних, і від залежних змінних
до нерівностей). І двоїсті задачі для максимізації і мінімізації
виходять різні…}
TDualTaskType=(dt_MaxToMin, dt_MinToMax);
{Процедури для форматування екранної таблиці
GrowingStringGrid
під час
роботи з нею у потрібному форматі, а також для вирішування
задач ЛП і відображення проміжних чи кінцевих результатів у
такій таблиці:}
TGridFormattingProcs=class(TObject)
Private
{Робочі масиви:}
CurHeadRow, CurHeadCol:TValOrNameMas; {заголовки таблиці}
CurTable:TFloatMatrix; {таблиця}
{Масиви для зберігання умови (використовуються для
багатокритеріальної задачі):}
CopyHeadRow, CopyHeadCol:TValOrNameMas; {заголовки таблиці}
CopyTable:TFloatMatrix; {таблиця}
InSolving, SolWasFound, WasNoRoots, WasManyRoots,
EqM1TaskPrepared, EqM2TaskPrepared, LTaskPrepared: Boolean;
{Прапорець про те, що вміст CurGrid ще не був прочитаний
даним об'єктом з часу останнього редагування його користуваем:}
CurGridModified: Boolean;
{В режимах розв'язування (CurFormatState=fs_SolvingEqsM1,
fs_SolvingEqsM2, fs_SolvingLTask)
– координати розв'язувальної комірки у GrowingStringGrid
(відносно екранної таблиці);
в режимах редагування (CurFormatState=fs_EnteringEqs, fs_EnteringLTask)
–
координати комірки, для якої викликано контекстне меню
(відносно верхньої лівої комірки таблиці коефіцієнтів (що має
тут координати [0,0])):}
CurGridSolveCol, CurGridSolveRow: Integer;
{Номери стовпця і рядка-заголовків у
CurGrid
:}
CHeadColNum, CHeadRowNum: Integer;
{Режим форматування і редагування чи розв'язування задачі:}
CurFormatState:TTableFormatState;
{Екранна таблиця для редагування чи відображення результатів:}
CurGrid:TGrowingStringGrid;
CurOutConsole:TMemo; {поле для відображення повідомлень}
{Адреси обробників подій екранної таблиці
CurGrid
, які цей
об'єкт заміняє своїми власними:}
OldOnNewCol:TNewColEvent;
OldOnNewRow:TNewRowEvent;
OldOnDrawCell:TDrawCellEvent;
OldOnDblClick:TNotifyEvent;
OldOnMouseUp:TMouseEvent;
OldOnSetEditText:TSetEditEvent;
{Процедура встановлює довжину рядка-заголовка CurHeadRow відповідно
до ширини екранної таблиці CurGrid і заповнює нові елементи
значеннями за змовчуванням. Використовується при зміні розмірів
екранної таблиці. Після її виклику можна вказувати типи змінних
у рядку-заголовку (користувач вибирає залежні та незалежні):}
ProcedureUpdateLTaskHeadRowToStrGrid (SGrid:TStringGrid);
{Процедура для підтримки масиву стовпця-заголовка під час
редагування таблиці. Встановлює довжину масиву відповідно до висоти
екранної таблиці і координат вписування в неї таблиці задачі,
заповнює нові комірки значеннями за змовчуванням:}
Procedure UpdateLTaskHeadColToStrGrid (SGrid:TStringGrid;
NewRows: array of Integer);
{Функції для переходів з одного режиму до іншого:}
Procedure SetNewState (Value:TTableFormatState);
Function PrepareToSolveEqsWithM1: Boolean;
Function PrepareToSolveEqsWithM2: Boolean;
Function PrepareToSolveLTask: Boolean;
Procedure SetNewGrid (Value:TGrowingStringGrid); {перехід до нового CurGrid}
Procedure SetNewMemo (Value:TMemo); {перехід до нового CurOutConsole}
{Процедури форматування GrowingStringGrid для набору таблиці
лінійних рівнянь:}
procedure EditLineEqsOnNewRow (Sender: TObject; NewRows: array of Integer);
procedure EditLineEqsOnNewCol (Sender: TObject; NewCols: array of Integer);
procedure EditLineEqsOnDrawCell (Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
{Процедура форматування GrowingStringGrid відображення таблиці
у процесі розв'язання системи рівнянь способом 1 і 2:}
procedure SolveLineEqsM1OrM2OnDrawCell (Sender: TObject;
ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
{Процедури форматування GrowingStringGrid для набору таблиці
задачі максимізації чи мінімізації лінійної форми (функції з
умовами-нерівностями чи рівняннями):}
procedure EdLineTaskOnNewRow (Sender: TObject; NewRows: array of Integer);
procedure EdLineTaskOnNewCol (Sender: TObject; NewCols: array of Integer);
procedure EdLineTaskOnDrawCell (Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
procedure EdLineTaskOnDblClick (Sender: TObject);
{Процедура реагує на відпускання правої кнопки миші на
комірках рядка-заголовка та стовпця-заголовка таблиці.
Формує та відкриває контекстне меню для вибору типу комірки із можливих
типів для цієї комірки:}
procedure EdLineTaskOnMouseUp (Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
{Процедура перевіряє наявність об'єкта TPopupMenu. Якщо його немає
(SGrid. PopupMenu=Nil), то створює новий.
Видаляє усі пунтки (елементи, теми) з меню:}
ProcedureInitGridPopupMenu (SGrid:TStringGrid);
{Додає пункт меню для вибору типу комірки в таблиці з заданим
написом
SCaption
і кругом того кольору, що асоційований з даним
типом
SAssocType
. Для нового пункту меню настроює виклик
процедури обробки комірки для задавання їй обраного типу
SAssocType
.
Значення
SAssocType
записує у поле
Tag
об'єкта пункту меню:}
Procedure AddCellTypeItemToMenu (SMenu:TPopupMenu;
SCaption: String; IsCurrentItem: Boolean; SAssocType:THeadLineElmType;
ToSetReactOnClick: Boolean=True);
{Обробник вибору пункту в меню типів для комірки
рядка – чи стовпця-заголовка.}
Procedure ProcOnCellTypeSelInMenu (Sender: TObject);
{Процедури для нумерації рядків і стовпців при відображенні
таблиць у ході вирішення задачі, або з результатами. Лише
проставляють номери у першому стовпцю і першому рядку:}
procedure NumerationOnNewRow (Sender: TObject; NewRows: array of Integer);
procedure NumerationOnNewCol (Sender: TObject; NewCols: array of Integer);
{Процедура для реагування на редагування вмісту комірок
під час редагування вхідних даних. Встановлює прапорець
CurGridModified
:=
True
про те, що екранна таблиця має зміни:}
procedure ReactOnSetEditText (Sender: TObject; ACol, ARow: Longint;
const Value: string);
{Зчитує комірку з екранної таблиці в рядок-заголовок.
Вхідні дані:
SCol
– номер комірки у рядку-заголовку.
Для екранної таблиці використовуються координати комірки відповідно до
координат рядка-заголовка та стовпця заголовка (верхнього лівого кута
таблиці з заголовками):
HeadColNumInGrid
і
HeadRowNumInGrid
:}
ProcedureReadHeadRowCell (SCol: Integer);
{Зчитує комірку з екранної таблиці в стовпець-заголовок.
Вхідні дані:
SRow
– номер комірки у стовпці-заголовку.
Для екранної таблиці використовуються координати комірки відповідно до
координат рядка-заголовка та стовпця заголовка (верхнього лівого кута
таблиці з заголовками):
HeadColNumInGrid
і
HeadRowNumInGrid
:}
ProcedureReadHeadColCell (SRow: Integer);
{Процедура для зчитування таблиці та її заголовків із
CurGrid
:}
FunctionReadTableFromGrid: Boolean;
{Процедура для відображення таблиці та її заголовків у
CurGrid
:}
Function WriteTableToGrid (SHeadColNum, SHeadRowNum: Integer;
ToTuneColWidth: Boolean=True):Boolean;
{Визначення розмірів таблиці задачі, і корегування довжини
заголовків таблиці та зовнішнього масиву таблиці (масиву масивів):}
Procedure GetTaskSizes (Var DWidth, DHeight: Integer);
{Жорданове виключення за заданим розв'язувальним елементом матриці:}
Function GI (RozElmCol, RozElmRow: Integer;
Var SDHeadRow, SDHeadCol:TValOrNameMas; Var SDMatrix:TFloatMatrix;
Var DColDeleted: Boolean; ToDoMGI: Boolean=False;
ToDelColIfZeroInHRow: Boolean=True):Boolean;
{Відображення таблиці, обробка віконних подій доки користувач не
скомандує наступний крок (якщо користувач не скомандував вирішувати
до кінця):}
Procedure WaitForNewStep (HeadColNum, HeadRowNum: Integer);
{Пошук ненульової розв'язувальної комірки для вирішування системи
рівнянь (починаючи з комірки [
CurRowNum
,
CurColNum
]):}
Function SearchNozeroSolveCell (CurRowNum,
CurColNum, MaxRow, MaxCol: Integer;
HeadRowNum, HeadColNum: Integer;
ToSearchInRightColsToo: Boolean=True):Boolean;
{Зміна знаків у рядку таблиці і відповідній комірці у
стовпці-заголовку:}
Procedure ChangeSignsInRow (CurRowNum: Integer);
{Зміна знаків у стовпці таблиці і відповідній комірці у
рядку-заголовку:}
Procedure ChangeSignsInCol (CurColNum: Integer);
{Функція переміщує рядки таблиці CurTable (разом із відповідними
комірками у стовпці-заголовку
CurHeadCol
) з заданими типами комірок
стовпця-заголовка вгору.
Повертає номер найвищого рядка із тих, що не було задано
переміщувати вгору (вище нього – ті, що переміщені вгору):}
Function ShiftRowsUp (SHeadColElmTypes:THeadLineElmTypes;
ToChangeInitPosNums: Boolean=False):Integer;
{Аналогічна до
ShiftRowsUp
, але переміщує вниз.
Повертає номер найвищого рядка із тих, що переміщені вниз (вище
нього – рядки тих типів, що не було задано переміщувати донизу):}
Function ShiftRowsDown (
SHeadColElmTypes:THeadLineElmTypes;
ToChangeInitPosNums: Boolean=False):Integer;
{Вирішування системи лінійних рівнянь способом 1:}
FunctionSolveEqsWithM1: Boolean;
{Вирішування системи лінійних рівнянь способом 2:}
FunctionSolveEqsWithM2: Boolean;
{Вирішування задачі максимізації лінійної форми (що містить
умови-нерівності, рівняння та умови на невід'ємність окремих
змінних і одну функцію мети, для якої треба знайти максимальне
значення):}
Function SolveLTaskToMax (DualTaskVals: Boolean):Boolean;
Function PrepareDFuncForSimplexMaximize: Boolean;
Function PrepareDestFuncInMultiDFuncLTask (SFuncRowNum,
MinDestFuncRowNum: Integer):Boolean;
{Процедура зчитує значення функції мети у таблиці розв'язаної
однокритеріальної задачі, і значення усіх змінних або функцій
в цьому розв'язку. Відображає значення цих змінних,
функцій-нерівностей, і функції мети в
Self
.
CurOutConsole:}
Procedure ShowLTaskResultCalc (DualTaskVals: Boolean);
{Процедура зчитує значення функції мети у таблиці розв'язаної
однокритеріальної задачі, і значення усіх змінних або функцій в
цьому розв'язку:}
Procedure ReadCurFuncSolution (Var SDValVecs:TFloatMatrix;
Var SDDestFuncVals:TFloatArr; SVecRow: Integer;
ToReadFuncVals: Boolean; DualTaskVals: Boolean);
Procedure BuildPaymentTaskOfOptim (
Const SOptimXVecs:TFloatMatrix; Const SOptimFuncVals:TFloatArr;
SFirstDFuncRow: Integer);
Procedure CalcComprVec (Const SVarVecs:TFloatMatrix;
Const SWeightCoefs:TFloatArr; Var DComprVec:TFloatArr);
Function CalcDFuncVal (Const SVarVec:TFloatArr;
SDestFuncRowNum: Integer):TWorkFloat;
{Вирішування задачі багатокритеріальної оптимізації лінійної
форми з використанням теоретико-ігрового підходу.
Умовою задачі є умови-нерівності, рівняння та умови на
невід'ємність окремих змінних, і декілька функцій мети, для
яких треба знайти якомога більші чи менші значення.
Функція повертає ознаку успішності вирішування:}
FunctionSolveMultiCritLTask: Boolean;
{Процедури для зміни позиціювання таблиці з заголовками у
екранній таблиці
CurGrid
. Працюють лише у режимі
fs
_
FreeEdit
:}
Procedure SetHeadColNum (Value: Integer);
Procedure SetHeadRowNum (Value: Integer);
public
{Прапорці для керування кроками вирішування:
Continue
– продовжити на один крок;
GoToEnd
– при продовженні йти всі кроки до кінця вирішування без
відображення таблиці на кожному кроці;
Stop
– припинити вирішування.
Для керування прапорці можуть встановлюватися іншими потоками
програми, або і тим самим потоком (коли процедури даного класу
викликають Application. ProcessMessages):}
Continue, GoToEnd, Stop: Boolean;
{Властивість для керуання станом форматування:}
Property TableFormatState:TTableFormatState read CurFormatState
write SetNewState default fs_NoFormatting;
{Прапорець про те, що зараз задача у ході вирішування
(між кроками вирішування):}
Property Solving: Boolean read InSolving;
Property SolutionFound: Boolean read SolWasFound;
Property NoRoots: Boolean read WasNoRoots;
Property ManyRoots: Boolean read WasManyRoots;
{Властивість для задавання екранної таблиці:}
Property StringGrid:TGrowingStringGrid read CurGrid write SetNewGrid
defaultNil;
{Поле для відображення повідомлень:}
Property MemoForOutput:TMemo read CurOutConsole write SetNewMemo
defaultNil;
{Номери стовпця і рядка-заголовків у
CurGrid
. Змінювати можна
тільки у режимі
fs
_
FreeEdit
. В інших режимах зміна ігнорується:}
Property HeadColNumInGrid: Integer read CHeadColNum write SetHeadColNum;
Property HeadRowNumInGrid: Integer read CHeadRowNum write SetHeadRowNum;
{Таблиця і її заголовки у пам'яті:}
Property Table:TFloatMatrix read CurTable;
Property HeadRow:TValOrNameMas read CurHeadRow;
Property HeadCol:TValOrNameMas read CurHeadCol;
{Читання і запис таблиці та режиму редагування у файл
(тільки у режимах редагування):}
Function ReadFromFile (Const SPath: String):Boolean;
Function SaveToFile (Const SPath: String):Boolean;
{Процедури для читання і зміни таблиці і її заголовків.
Не рекомендується застосовувати під час вирішування
(при Solving=True):}
Procedure SetTable (Const SHeadRow, SHeadCol:TValOrNameMas;
Const STable:TFloatMatrix);
Procedure GetTable (Var DHeadRow, DHeadCol:TValOrNameMas;
Var DTable:TFloatMatrix);
{Вибір кольору для фону комірки за типом елемента
стовпця – або рядка-заголовка:}
Function GetColorByElmType (CurType:THeadLineElmType):TColor;
{Вибір назви комірки за типом елемента
стовпця – або рядка-заголовка:}
Function GetNameByElmType (CurType:THeadLineElmType):String;
{Зчитування умови задачі із
CurGrid
та відображення прочитаного
на тому ж місці, де воно було.
Працює у режимах
fs_EnteringEqs і fs_EnteringLTask.}
Function GetTask (ToPrepareGrid: Boolean=True):Boolean;
{Приймає останні зміни при редагуванні і відображає таблицю:}
Procedure Refresh;
Procedure ResetModified; {скидає прапорець зміненого стану}
Procedure UndoChanges; {відкидає останні зміни (ResetModified+Refresh)}
{Перехід від зчитаної умови задачі максимізації чи мінімізації
лінійної форми до двоїстої задачі. Працює у режимі редагування
задачі максимізації-мінімізації (
fs
_
EnteringLTask
):}
FunctionMakeDualLTask: Boolean;
{Розміри прочитаної таблиці задачі:}
Function TaskWidth: Integer;
Function TaskHeight: Integer;
{Запускач вирішування. Працює у режимах fs_SolvingEqsM1,
fs_SolvingEqsM2, fs_SolvingLTask:}
Function Solve (ToGoToEnd: Boolean=False):Boolean;
Constructor Create;
Destructor Free;
End;
{Визначає знак дійсного числа:}
Function ValSign (Const Value:TWorkFloat):TSignVal; overload;
Function ValSign (Const Value:TValOrName):TSignVal; overload;
Function GetValOrNameAsStr (Const Value:TValOrName):String;
Procedure ChangeSignForValOrVarName (Var SDValOrName:TValOrName);
Procedure DeleteFromArr (Var SArr:TValOrNameMas; Index, Count: Integer);
overload;
Procedure DeleteFromArr (Var SArr:TFloatArr; Index, Count: Integer); overload;
Procedure DelColsFromMatr (Var SDMatrix:TFloatMatrix; ColIndex, Count: Integer);
Procedure DelRowsFromMatr (Var SDMatrix:TFloatMatrix; RowIndex, Count: Integer);
Procedure ChangeRowsPlaces (Var SDMatr:TFloatMatrix; Row1, Row2: Integer);
overload;
Procedure ChangeRowsPlaces (Var SDMatr:TFloatMatrix;
Var SDHeadCol:TValOrNameMas; Row1, Row2: Integer;
ToChangeInitPosNums: Boolean=False); overload;
Procedure ChangeColsPlaces (Var SDMatr:TFloatMatrix; Col1, Col2: Integer);
overload;
Procedure ChangeColsPlaces (Var SDMatr:TFloatMatrix;
Var SDHeadRow:TValOrNameMas; Col1, Col2: Integer;
ToChangeInitPosNums: Boolean=False); overload;
{Транспонування двовимірної матриці:}
Procedure Transpose (Var SDMatrix:TFloatMatrix);
implementation
const
sc_InvCoordsOfResolvingElm=
'Немає розв''язуючого елемента з такими координатами';
sc_ZeroResolvingElm='Розв''язуючий елемент рівний нулю';
sc_MatrixSize='Розміри матриці';
sc_NoGrowingStringGrid='GrowingStringGrid не заданий' + sc_TriSpot;
sc_UnknownVarType='Невідомий тип змінної';
sc_TableIsNotReady=': таблиця не готова' + sc_TriSpot;
sc_WrongEditMode=': не той режим редагування'+
' задачі. Не можу перейти до розв''язування' + sc_TriSpot;
sc_EmptyTable=': таблиця пуста' + sc_TriSpot;
sc_CantReadTaskInCurMode=
': у поточному режимі умова задачі не зчитується';
sc_CantWriteTaskInCurMode=
': не можу записати умову задачі з поточного режиму'+sc_TriSpot;
sc_CantCloseFile=': не можу закрити файл:'+sc_DoubleQuot;
sc_StartSolving=': починаю розв''язування' + sc_TriSpot;
sc_ZeroKoef=': нульовий коефіцієнт';
sc_SearchingOther=' шукаю інший' + sc_TriSpot;
sc_AllKoefIsZeroForVar=': усі коефіцієнти є нулі для змінної';
sc_AllKoefIsZero=': усі коефіцієнти для потрібних змінних є нулі'+sc_TriSpot;
sc_FreeVar=': вільна змінна (у її стовпці лише нулі, не впливає на результат)';
sc_NoRoots='Коренів немає.';
sc_NoVals='Значень немає.';
sc_ManyRoots='Коренів безліч.';
sc_UnlimitedFunc='Функція мети не обмежена.';
sc_SolutionFound='Корені знайдено.';
sc_ValFound='Значення знайдено.';
sc_SolvingStopped=': розв''язування припинено' + sc_TriSpot;
sc_ExcludingFreeVars=': виключаю незалежні змінні' + sc_TriSpot;
sc_CantExcludeFreeVars=': не можу виключити усі незалежні змінні.'+
sc_Space+sc_UnlimitedFunc;
sc_AllFreeVarsExcluded=': усі незалежні змінні виключені.';
sc_NoTableAreaToWork=
': Увага! У таблиці більше немає комірок для наступної обробки'+sc_TriSpot;
sc_ExcludingZeroRows=': виключаю 0-рядки' + sc_TriSpot;
sc_AllZeroInRow=': усі елементи – нулі у рядку';
sc_NoMNN=': не можу знайти МНВ для стовпця';
sc_AllZeroRowsExcluded=': усі 0-рядки виключені.';
sc_SearchingBaseSolve=': шукаю опорний розв''язок' + sc_TriSpot;
sc_BaseSolveFound=': опорний розв''язок знайдено.';
sc_SearchingOptimSolve=': шукаю оптимальний розв''язок' + sc_TriSpot;
sc_NoSolveMode=': поточний режим не є режимом для розв''язування'+sc_TriSpot;
sc_ValNotAvail='значення не доступно' + sc_TriSpot;
sc_ResultIs='Результат ';
sc_ForDualTask='для двоїстої задачі (відносно розв''язаної):';
sc_ForDirectTask='для прямої задачі:';
sc_InHeadRow='У рядку-заголовку:';
sc_InHeadCol='У стовпці-заголовку:';
sc_ResFunc='Функція мети:';
sc_CanMakeOnlyInELTaskMode='до двоїстої задачі можна переходити лише у '+
'режимі fs_EnteringLTask' + sc_TriSpot;
sc_CanMakeDTaskOnlyForOneDFunc=': можу переходити до двоїстої задачі ' +
'тільки від однокритеріальної задачі ЛП (з одною функцією мети). '+
'Всього функцій мети: ';
sc_CantChangeStateInSolving=
': не можу міняти режим під час розв''язування…';
sc_CantDetMenuItem=': не визначено пункт меню, який викликав процедуру…';
sc_UnknownObjectCall=': невідомий об''єкт, який викликав процедуру: клас ';
sc_NoCellOrNotSupported=': комірка не підтримується або не існує: ';
sc_Row='Рядок'; sc_Col='Стовпець';
sc_CantOpenFile=': не можу відкрити файл: «';
sc_EmptyFileOrCantRead=': файл пустий або не читається: «';
sc_FileNotFullOrHasWrongFormat=': файл не повний або не того формату: «';
sc_CantReadFile=': файл не читається: «';
sc_CantCreateFile=': не можу створити файл: «';
sc_CantWriteFile=': файл не вдається записати: «';
sc_CurRowNotMarkedAsDestFunc=
': заданий рядок не помічений як функція мети: рядок ';
sc_RowNumsIsOutOfTable=': задані номери рядків виходять за межі таблиці!..';
sc_NoDestFuncs=': немає рядків функцій мети! Задачу не розумію…';
sc_OnlyDestFuncsPresent=': у таблиці всі рядки є записами функцій мети!..';
sc_ForDestFunc=': для функції: ';
sc_SearchingMin='шукаю мінімум';
sc_SearchingMax='шукаю максимум';
sc_CalculatingNoOptMeasures=': підраховую міри неоптимальності…';
sc_AllMeasurIsZero=': усі міри рівні нулю, додаю до них одиницю…';
sc_UniqueMeasureCantSetZero=': є тільки одна міра оптимальності (і одна'+
' функція мети). Максимальна за модулем – вона ж. Додавання цієї'+
' максимальної величини замінить її на нуль. Тому заміняю на одиницю…';
sc_WeightCoefs='Вагові коефіцієнти (Li[Func]=ui/W(U)):';
sc_ComprVarVals='Компромісні значення змінних';
sc_DestFuncComprVals='Компромісні значення функцій мети:';
Function ValSign (Const Value:TWorkFloat):TSignVal; overload;
Var Res1:TSignVal;
Begin
Res1:=bc_Zero;
If Value<0 then Res1:=bc_Negative
Else if Value>0 then Res1:=bc_Positive;
ValSign:=Res1;
End;
Function ValSign (Const Value:TValOrName):TSignVal; overload;
Var Res1:TSignVal;
Begin
If Value. ElmType=bc_Number then
Res1:=ValSign (Value. AsNumber)
Else
Begin
If Pos (sc_Minus, Value. AsVarName)=1 then Res1:=bc_Negative
Else Res1:=bc_Positive;
End;
ValSign:=Res1;
End;
Function GetValOrNameAsStr (Const Value:TValOrName):String;
Begin
If Value. ElmType=bc_Number then
GetValOrNameAsStr:=FloatToStr (Value. AsNumber)
Else GetValOrNameAsStr:=Value. AsVarName;
End;
Procedure DeleteFromArr (Var SArr:TValOrNameMas; Index, Count: Integer); overload;
{Процедура для видалення з одновимірного масиву чисел чи назв змінних
SArr
одного або більше елементів, починаючи з елемента з номером
Index
.
Видаляється
Count
елементів (якщо вони були у масиві починаючи із елемента
з номером Index).}
Var CurElm: Integer;
Begin
If Count<=0 then Exit; {якщо немає елементів для видалення}
{Якщо є хоч один елемент із заданих для видалення:}
If Length(SArr)>=(Index+1) then
Begin
{Якщо у масиві немає так багато елементів, скільки холіли видалити, то
коригуємо кількість тих, що видаляємо:}
If (Index+Count)>Length(SArr) then Count:=Length(SArr) – Index;
{Зсуваємо елементи масиву вліво, що залишаються справа після видалення
заданих:}
For CurElm:=Index to (Length(SArr) – 1-Count) do
SArr[CurElm]:=SArr [CurElm+Count];
{Видаляємо з масиву зайві елементи справа:}
SetLength (SArr, Length(SArr) – Count);
End;
End;
Procedure DeleteFromArr (Var SArr:TFloatArr; Index, Count: Integer); overload;
{Процедура для видалення з одновимірного масиву дійсних чисел
SArr
одного або більше елементів, починаючи з елемента з номером
Index
.
Видаляється
Count
елементів (якщо вони були у масиві починаючи із елемента
з номером Index).}
Var CurElm: Integer;
Begin
If Count<=0 then Exit; {якщо немає елементів для видалення}
{Якщо є хоч один елемент із заданих для видалення:}
If Length(SArr)>=(Index+1) then
Begin
{Якщо у масиві немає так багато елементів, скільки холіли видалити, то
коригуємо кількість тих, що видаляємо:}
If (Index+Count)>Length(SArr) then Count:=Length(SArr) – Index;
{Зсуваємо елементи масиву вліво, що залишаються справа після видалення
заданих:}
For CurElm:=Index to (Length(SArr) – 1-Count) do
SArr[CurElm]:=SArr [CurElm+Count];
{Видаляємо з масиву зайві елементи справа:}
SetLength (SArr, Length(SArr) – Count);
End;
End;
Procedure DelColsFromMatr (Var SDMatrix:TFloatMatrix; ColIndex, Count: Integer);
{Процедура для видалення із матриці дійсних чисел
SHeadArr
одного або більше стовпців, починаючи зі стовпця з номером
ColIndex
.
Видаляється
Count
стовпців (якщо вони були у матриці починаючи зі стовпця
з номером ColIndex).}
Var CurRow: Integer;
Begin
If Count<=0 then Exit; {якщо немає елементів для видалення}
{Видаляємо елементи у вказаних стовпцях з кожного рядка.
Так
видалимо стовпці:}
For CurRow:=0 to (Length(SDMatrix) – 1) do
Begin
DeleteFromArr (SDMatrix[CurRow], ColIndex, Count);
End;
End;
Procedure DelRowsFromMatr (Var SDMatrix:TFloatMatrix; RowIndex, Count: Integer);
{Процедура для видалення із матриці дійсних чисел
SHeadArr
одного або більше рядків, починаючи з рядка з номером
RowIndex
.
Видаляється
Count
рядків (якщо вони були у матриці починаючи з рядка
з номером RowIndex).}
Var CurElm: Integer;
Begin
If Count<=0 then Exit; {якщо немає елементів для видалення}
{Якщо є хоч один рядок із заданих для видалення:}
If Length(SDMatrix)>=(RowIndex+1) then
Begin
{Якщо у матриці немає так багато рядків, скільки холіли видалити, то
коригуємо кількість тих, що видаляємо:}
If (RowIndex+Count)>Length(SDMatrix) then Count:=Length(SDMatrix) – RowIndex;
{Зсуваємо рядки матриці вгору, що залишаються знизу після видалення
заданих:}
For CurElm:=RowIndex to (Length(SDMatrix) – 1-Count) do
SDMatrix[CurElm]:=SDMatrix [CurElm+Count];
{Видаляємо з матриці зайві рядки знизу:}
SetLength (SDMatrix, Length(SDMatrix) – Count);
End;
End;
Procedure ChangeSignForValOrVarName (Var SDValOrName:TValOrName);
{Зміна знаку числа або перед іменем змінної:}
Begin
If SDValOrName. ElmType=bc_Number then {для числа:}
SDValOrName. AsNumber:=-SDValOrName. AsNumber
Else {для рядка-назви:}
Begin
If Pos (sc_Minus, SDValOrName. AsVarName)=1 then
Delete (SDValOrName. AsVarName, 1, Length (sc_Minus))
Else SDValOrName. AsVarName:=sc_Minus+SDValOrName. AsVarName;
End;
End;
{Жорданове виключення за заданим розв'язувальним елементом матриці:}
Function TGridFormattingProcs.GI (RozElmCol, RozElmRow: Integer;
Var SDHeadRow, SDHeadCol:TValOrNameMas; Var SDMatrix:TFloatMatrix;
Var DColDeleted: Boolean;
ToDoMGI: Boolean=False; {прапорець на модифіковане Жорданове виключення}
ToDelColIfZeroInHRow: Boolean=True):Boolean;
{Функція виконує Жорданове виключення для елемента матриці
SDMatrix з координатами (RozElmCol, RozElmRow). Окрім обробки матриці,
здійснюється заміна місцями елементів у рядку і стовпцю-заголовках
матриці (SDHeadRow, SDHeadCol).
Вхідні дані:
RozElmCol
– номер стовпця матриці, у якому лежить розв'язувальний елемент.
нумерація з нуля;
RozElmRow
– номер рядка матриці, у якому лежить розв'язувальний елемент.
нумерація з нуля.
Розв'язувальний елемент не повинен бути рівним нулю, інакше виконання
Жорданового виключення не можливе;
SDHeadRow
,
SDHeadCol
– рядок і стовпець-заголовки матриці. Рядок-заголовок
SDHeadRow
повинен мати не менше елементів, ніж є ширина матриці. Він
містить множники. Стовпець-заголовок
SDHeadCol
повинен бути не коротшим
за висоту матриці. Він містить праві частини рівнянь (чи нерівностей)
системи. Рівняння полягають у тому що значення елементів
стовпця-заголовка прирівнюються до суми добутків елементів відповідного
рядка матриці і елементів рядка-заголовка. Елементи у цих заголовках
можуть бути числами або рядками-іменами змінних. Якщо довжина
рядка-заголовка менша за ширину або стовпця-заголовка менша за висоту
матриці, то частина комірок матриці, що виходять за ці межі, буде
проігнорована;
SDMatrix
– матриця, у якій виконується Жорданове виключення;
ToDoMGI
– прапорець, що вмикає режим модифікованого Жорданового виключення
(при
ToDoMGI
=
True
здійснюється модифіковане, інакше – звичайне).
Модифіковане Жорданове виключення використовується для матриці, у якій
було змінено знак початкових елементів, і змінено знаки елементів-
множників у рядку-заголовку. Використовується для симплекс-методу.
ToDelColIfZeroInHRow
– прапорець, що вмикає видалення стовпця матриці із
розв'язувальним елементом, якщо після здійснення жорданівського
виключення у рядок-заголовок зі стовпця-заголовка записується число нуль.
Вихідні дані:
SDHeadRow
,
SDHeadCol
– змінені рядок та стовпець-заголовки. У них
міняються місцями елементи, що стоять навпроти розв'язувального елемента
(у його стовпці (для заголовка-рядка) і рядку (для заголовка-стовпця).
У заголовку-рядку такий елемент після цього може бути видалений, якщо
він рівний нулю і
ToDelColIfZeroInHRow
=
True
.
Тобто Жорданове виключення змінює ролями ці елементи (виражає один
через інший у лінійних рівняннях чи нерівностях);
SDMatrix
– матриця після виконання Жорданового виключення;
DColDeleted
– ознака того, що при виконанні Жорданового виключення
був видалений розв'язувальний стовпець із матриці (у його комірці
у рядку-заголовку став був нуль).
Функція повертає ознаку успішності виконання Жорданового виключення.
}
Var CurRow, CurCol, RowCount, ColCount: Integer;
SafeHeadElm:TValOrName;
MultiplierIfMGI:TWorkFloat;
CurMessage: String;
Begin
{Визначаємо кількість рядків і стовпців, які можна обробити:}
RowCount:=Length(SDMatrix);
If RowCount<=0 then Begin GI:=False; Exit; End;
ColCount:=Length (SDMatrix[0]);
If Length(SDHeadCol)<RowCount then RowCount:=Length(SDHeadCol);
If Length(SDHeadRow)<ColCount then ColCount:=Length(SDHeadRow);
If (RowCount<=0) or (ColCount<=0) then Begin GI:=False; Exit; End;
{Перевіряємо наявність розв'язуючого елемента у матриці (за координатами):}
If (RozElmCol>(ColCount-1)) or (RozElmRow>(RowCount-1)) then
Begin
CurMessage:=sc_InvCoordsOfResolvingElm+': ['+IntToStr (RozElmCol+1)+';'+
IntToStr (RozElmRow+1)+']'+sc_CrLf+
sc_MatrixSize+': ['+IntToStr(ColCount)+';'+IntToStr(RowCount)+']';
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
GI:=False; Exit;
End;
{Якщо розв'язуючий елемент рівний нулю, то виконати Жорданове виключення
неможливо:}
If SDMatrix [RozElmRow, RozElmCol]=0 then
Begin
CurMessage:=sc_ZeroResolvingElm+': ['+IntToStr (RozElmCol+1)+';'+
IntToStr (RozElmRow+1)+']='+FloatToStr (SDMatrix[RozElmRow, RozElmCol]);
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
GI:=False; Exit;
End;
{Виконуємо Жорданове виключення у матриці:}
{Обробляємо усі елементи матриці, що не належать до рядка і стовпця
розв'язуючого елемента:}
For CurRow:=0 to RowCount-1 do
For CurCol:=0 to ColCount-1 do
If (CurRow<>RozElmRow) and (CurCol<>RozElmCol) then
Begin
SDMatrix [CurRow, CurCol]:=
(SDMatrix [CurRow, CurCol]*SDMatrix [RozElmRow, RozElmCol] –
SDMatrix [CurRow, RozElmCol]*SDMatrix [RozElmRow, CurCol]) /
SDMatrix [RozElmRow, RozElmCol];
End;
{+1, якщо задано зробити звичайне Жорданове виключення;
-1 – якщо задано модифіковане:}
MultiplierIfMGI:=(1–2*Abs (Ord(ToDoMGI)));
{Елементи стовпця розв'язуючого елемента (окрім його самого)
ділимо на розв'язуючий елемент:}
For CurRow:=0 to RowCount-1 do
If CurRow<>RozElmRow then
SDMatrix [CurRow, RozElmCol]:=MultiplierIfMGI*SDMatrix [CurRow, RozElmCol]/
SDMatrix [RozElmRow, RozElmCol];
{Елементи рядка розв'язуючого елемента (окрім його самого)
ділимо на розв'язуючий елемент з протилежним знаком:}
For CurCol:=0 to ColCount-1 do
If CurCol<>RozElmCol then
SDMatrix [RozElmRow, CurCol]:=-MultiplierIfMGI*SDMatrix [RozElmRow, CurCol]/
SDMatrix [RozElmRow, RozElmCol];
{Заміняємо розв'язуючий елемент на обернене до нього число:}
SDMatrix [RozElmRow, RozElmCol]:=1/SDMatrix [RozElmRow, RozElmCol];
{Міняємо місцями елементи рядка і стовпця-заголовків, що стоять у
стовпці і рядку розв'язуючого елемента:}
SafeHeadElm:= SDHeadRow[RozElmCol];
SDHeadRow[RozElmCol]:=SDHeadCol[RozElmRow];
SDHeadCol[RozElmRow]:=SafeHeadElm;
{Якщо виконуємо модиівковане Жорданове виключення, то змінюють
знаки і ці елементи, що помінялись місцями:}
If ToDoMGI then
Begin
ChangeSignForValOrVarName (SDHeadRow[RozElmCol]);
ChangeSignForValOrVarName (SDHeadCol[RozElmRow]);
End;
DColDeleted:=False;
{Якщо у рядку-заголовку навпроти розв'язуючого елемента опинився нуль,
і задано видаляти у такому випадку цей елемент разом із стовпцем
розв'язуючого елемента у матриці, то видаляємо:}
If ToDelColIfZeroInHRow and (SDHeadRow[RozElmCol].ElmType=bc_Number) then
If SDHeadRow[RozElmCol].AsNumber=0 then
Begin
DeleteFromArr (SDHeadRow, RozElmCol, 1);
DelColsFromMatr (SDMatrix, RozElmCol, 1);
DColDeleted:=True;
End;
GI:=True;
End;
Procedure ChangeRowsPlaces (Var SDMatr:TFloatMatrix; Row1, Row2: Integer);
overload;
Var SafeCurRow:TFloatArr;
Begin
SafeCurRow:=SDMatr[Row1];
SDMatr[Row1]:=SDMatr[Row2];
SDMatr[Row2]:=SafeCurRow;
End;
Procedure ChangeRowsPlaces (Var SDMatr:TFloatMatrix; Var SDHeadCol:TValOrNameMas;
Row1, Row2: Integer; ToChangeInitPosNums: Boolean=False); overload;
{Процедура міняє місцями рядки у таблиці зі стовпцем-заголовком.
Вхідні дані:
SDMatr
– таблиця;
SDHeadCol
– стовпець-заголовок таблиці;
Row
1,
Row
2 – рядки, що треба поміняти місцями;
ToChangeInitPosNums
– вмикач зміни номерів по порядку у
стовпці-заголовку. Якщо рівний
True
, то рядки, що помінялися місцями,
міняються також і позначками про номер по порядку та розміщення
як рядка чи стовпця (що присвоювалися їм при створенні).
Вихідні дані:
SDMatr
– таблиця;
SDHeadCol
– стовпець-заголовок таблиці.}
Var SafeCurHeadCell:TValOrName;
Begin
SafeCurHeadCell:=SDHeadCol[Row1];
SDHeadCol[Row1]:=SDHeadCol[Row2];
SDHeadCol[Row2]:=SafeCurHeadCell;
If ToChangeInitPosNums then
Begin
SDHeadCol[Row2].VarInitPos:=SDHeadCol[Row1].VarInitPos;
SDHeadCol[Row2].VarInitInRow:=SDHeadCol[Row1].VarInitInRow;
SDHeadCol[Row1].VarInitPos:=SafeCurHeadCell. VarInitPos;
SDHeadCol[Row1].VarInitInRow:=SafeCurHeadCell. VarInitInRow;
End;
ChangeRowsPlaces (SDMatr, Row1, Row2);
End;
Procedure ChangePlaces (Var SDMas:TFloatArr; Elm1, Elm2: Integer);
Var SafeElm:TWorkFloat;
Begin
SafeElm:=SDMas[Elm1];
SDMas[Elm1]:=SDMas[Elm2];
SDMas[Elm2]:=SafeElm;
End;
Procedure ChangeColsPlaces (Var SDMatr:TFloatMatrix; Col1, Col2: Integer);
overload;
Var CurRow: Integer;
Begin
For CurRow:=0 to Length(SDMatr) – 1 do
ChangePlaces (SDMatr[CurRow], Col1, Col2);
End;
Procedure ChangeColsPlaces (Var SDMatr:TFloatMatrix; Var SDHeadRow:TValOrNameMas;
Col1, Col2: Integer; ToChangeInitPosNums: Boolean=False); overload;
{Процедура міняє місцями стовпці у таблиці з рядком-заголовком.
Вхідні дані:
SDMatr
– таблиця;
SDHeadRow
– рядок-заголовок таблиці;
Row
1,
Row
2 – рядки, що треба поміняти місцями;
ToChangeInitPosNums
– вмикач зміни номерів по порядку у
стовпці-заголовку. Якщо рівний
True
, то рядки, що помінялися місцями,
міняються також і позначками про номер по порядку та розміщення
як рядка чи стовпця (що присвоювалися їм при створенні).
Вихідні дані:
SDMatr
– таблиця;
SDHeadCol
– рядок-заголовок таблиці.}
Var SafeCurHeadCell:TValOrName;
Begin
SafeCurHeadCell:=SDHeadRow[Col1];
SDHeadRow[Col1]:=SDHeadRow[Col2];
SDHeadRow[Col2]:=SafeCurHeadCell;
If ToChangeInitPosNums then
Begin
SDHeadRow[Col2].VarInitPos:=SDHeadRow[Col1].VarInitPos;
SDHeadRow[Col2].VarInitInRow:=SDHeadRow[Col1].VarInitInRow;
SDHeadRow[Col1].VarInitPos:=SafeCurHeadCell. VarInitPos;
SDHeadRow[Col1].VarInitInRow:=SafeCurHeadCell. VarInitInRow;
End;
ChangeColsPlaces (SDMatr, Col1, Col2);
End;
Procedure TGridFormattingProcs. WaitForNewStep (HeadColNum, HeadRowNum: Integer);
{Зупиняє хід вирішування, відображає поточний стан таблиці, і чекає,
доки не буде встановлений один з прапорців:
Self. Continue, Self. GoToEnd або Self. Stop.
Якщо прапорці Self. GoToEnd або Self. Stop
вже були встановлені до
виклику цієї процедури, то процедура не чекає встановлення прапорців.}
Begin
{Якщо процедуру викликали, то треба почекати, доки не встановиться
Self
.
Continue
=
True
, незважаючи на поточний стан цього прапорця:}
Self. Continue:=False;
{Відображаємо поточний стан таблиці, якщо не ввімкнено режим
роботи без зупинок:}
If Not (Self. GoToEnd) then
Self. WriteTableToGrid (HeadColNum, HeadRowNum, True);
{Чекаємо підтвердження для наступного кроку, або переривання
розв'язування:}
While Not (Self. Continue or Self. GoToEnd or Self. Stop) do
Application. ProcessMessages;
End;
Function TGridFormattingProcs. SearchNozeroSolveCell (CurRowNum,
CurColNum, MaxRow, MaxCol: Integer;
HeadRowNum, HeadColNum: Integer;
ToSearchInRightColsToo: Boolean=True):Boolean;
{Пошук ненульової розв'язувальної комірки для вирішування системи рівнянь
або при вирішуванні задачі максимізації/мінімізації лінійної форми
симплекс-методом (починаючи з комірки [CurRowNum, CurColNum]).}
Const sc_CurProcName='SearchNozeroSolveCell';
Var CurSearchRowNum, CurSearchColNum: Integer;
st1: String;
Begin
{Якщо комірка, що хотіли взяти розв'язувальною, рівна нулю:}
If Self. CurTable [CurRowNum, CurColNum]=0 then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_ZeroKoef+
' ['+IntToStr (CurColNum+1)+'; '+IntToStr (CurRowNum+1)+']'+
sc_SearchingOther);
CurSearchRowNum:=MaxRow+1;
{Шукаємо ненульову комірку в заданій області (або в одному
її стовпці CurColNum, якщо ToSearchInRightColsToo=False):}
For CurSearchColNum:=CurColNum to MaxCol do
Begin
{Шукаємо ненульову комірку знизу у тому ж стовпцю:}
For CurSearchRowNum:=CurRowNum+1 to MaxRow do
Begin
If Self. CurTable [CurSearchRowNum, CurSearchColNum]<>0 then Break;
End;
{Якщо немає ненульових, то змінна вільна:}
If CurSearchRowNum>MaxRow then
Begin
If Self. CurOutConsole<>Nil then
Begin
st1:=sc_CurProcName+sc_AllKoefIsZeroForVar;
If Self. CurHeadRow[CurSearchColNum].ElmType=bc_Number then
st1:=st1+sc_Space+
FloatToStr (Self. CurHeadRow[CurSearchColNum].AsNumber)
Else st1:=st1+sc_Space+
sc_DoubleQuot+Self. CurHeadRow[CurSearchColNum].AsVarName+
sc_DoubleQuot;
Self. CurOutConsole. Lines. Add(st1);
End;
{Якщо потрібна комірка тільки у даному стовпці (для даної змінної),
то в інших стовцях не шукаємо:}
If Not(ToSearchInRightColsToo) then Break; {For CurSearchColNum…}
End
Else {Якщо знайдено ненульовий:}
Begin
Self. WaitForNewStep (HeadColNum, HeadRowNum);
{Якщо дано команду перервати розв'язування:}
If Self. Stop then
Begin
SearchNozeroSolveCell:=True; Exit;
End;
{Ставимо рядок із знайденим ненульовим замість поточного:}
ChangeRowsPlaces (Self. CurTable, Self. CurHeadCol, CurRowNum,
CurSearchRowNum);
{Якщо знайдена комірка у іншому стовпці, то міняємо місцями стовпці:}
If CurColNum<>CurSearchColNum then
ChangeColsPlaces (Self. CurTable, Self. CurHeadRow, CurColNum,
CurSearchColNum);
Break; {For CurSearchColNum:=CurColNum to MaxCol do…}
End;
End; {For CurSearchColNum:=CurColNum to MaxCol do…}
{Якщо ненульову комірку не знайдено:}
If (CurSearchColNum>MaxCol) or (CurSearchRowNum>MaxRow) then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_AllKoefIsZero);
SearchNozeroSolveCell:=False;
Exit; {задача не має розв'язків, або має їх безліч…}
End;
End; {If Self. CurTable [CurRowNum, CurColNum]=0 then…}
SearchNozeroSolveCell:=True;
End;
{Вирішування системи лінійних рівнянь способом 1:}
Function TGridFormattingProcs. SolveEqsWithM1: Boolean;
{Для таблиці виду:
x1 x2 x3… xn
a1
a2
a3
…
am}
Const sc_CurProcName='SolveEqsWithM1';
Var CurRowNum, CurColNum: Integer;
st1: String;
HeadRowNum, HeadColNum: Integer;
ColDeleted: Boolean;
Procedure ShowResultCalc;
{Відображає записи про обчислення значень змінних (у текстовому полі)
такого зказка:
<стовп1>=<
a
11>*<ряд1> + <
a
12>*<ряд2> +…
+ <a1n>*<рядn>;
…
<стовпm>=<am1>*<ряд1> + <am2>*<ряд2> +… + <amn>*<рядn>;
І підраховує значення, якщо можливо:
<стовп1>=<значення1>;
…
<стовп
m
>=<значення
m
>}
VarCurRowN, CurColN: Integer; ValueAvail: Boolean;
CurVal:TWorkFloat;
st2: String;
NotEqual, NoRoots: Boolean;
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_ResultIs+sc_DoubleSpot);
NoRoots:=False;
For CurRowN:=0 to Length (Self. CurHeadCol) – 1 do
Begin
st2:=''; ValueAvail:=True; CurVal:=0;
If Self. CurOutConsole<>Nil then
Begin
{<стовп i>=…:}
If Self. CurHeadCol[CurRowN].ElmType=bc_Number then
st2:=st2+FloatToStr (Self. CurHeadCol[CurRowN].AsNumber)
Else
st2:=st2+Self. CurHeadCol[CurRowN].AsVarName;
st1:=st2;
st1:=st1+sc_Space+sc_Equal+sc_Space; {=}
End;
For CurColN:=0 to Length (Self. CurHeadRow) – 1 do
Begin {(aij*:)
If Self. CurOutConsole<>Nil then
st1:=st1+sc_BrOp+FloatToStr (Self. CurTable [CurRowN, CurColN])+sc_Mul;
{рядj:}
If Self. CurHeadRow[CurColN].ElmType=bc_Number then
Begin
If Self. CurOutConsole<>Nil then
st1:=st1+FloatToStr (Self. CurHeadRow[CurColN].AsNumber);
If ValueAvail then CurVal:=CurVal +
Self. CurTable [CurRowN, CurColN]*Self. CurHeadRow[CurColN].AsNumber;
End
Else
Begin
If Self. CurOutConsole<>Nil then
st1:=st1+Self. CurHeadRow[CurColN].AsVarName;
ValueAvail:=False;
End;
If Self. CurOutConsole<>Nil then
Begin
st1:=st1+sc_BrCl; {)}
If CurColN<>(Length (Self. CurHeadRow) – 1) then
st1:=st1+sc_Space+sc_Plus+sc_Space {+}
Else st1:=st1+sc_KrKm; {;}
End;
End;
If Self. CurOutConsole<>Nil then
Begin
Self. CurOutConsole. Lines. Add(st1);
st1:=st2;
End;
If ValueAvail then
Begin
NotEqual:=False;
If Self. CurHeadCol[CurRowN].ElmType=bc_Number then
Begin
If Self. CurHeadCol[CurRowN].AsNumber<>CurVal then
Begin NoRoots:=True; NotEqual:=True; End;
End;
If Self. CurOutConsole<>Nil then
Begin
If NotEqual then
st1:=st1+sc_Space+sc_NotEqual+sc_Space {<>}
Else st1:=st1+sc_Space+sc_Equal+sc_Space; {=}
st1:=st1+FloatToStr(CurVal)+sc_KrKm; {<стовп i><V><значення>;}
End;
End
Else
Begin
If Self. CurOutConsole<>Nil then st1:=st1+sc_Space+sc_ValNotAvail;
Self. WasManyRoots:=True;
End;
If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add(st1);
End;
If NoRoots then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_NoRoots);
Self. WasManyRoots:=False;
End
Else if Not (Self. WasManyRoots) then Self. SolWasFound:=True;
Self. WasNoRoots:=NoRoots;
End;
Label LStopLabel;
Begin
If Self. TaskWidth<=0 then {Якщо таблиця пуста, то задача пуста:}
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_EmptyTable);
SolveEqsWithM1:=False;
Exit;
End;
HeadRowNum:=Self.CHeadRowNum;
HeadColNum:=Self.CHeadColNum;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_StartSolving);
CurRowNum:=0; {починаємо з першого рядка}
{Проходимо по усіх стовпцях (по усіх змінних), намагаючись брати
розв'язувальні комірки по головній діагоналі. Якщо серед таких зустрінеться
нуль, спробуємо знайти ненульову комірку нижче, і поміняти рядки нульової
з ненульовою, щоб ненульова стала на головній діагоналі:}
CurColNum:=0;
While (CurColNum<Length (Self. CurHeadRow)) and
(CurRowNum<Length (Self. CurHeadCol)) do
Begin
{Координати розв'язувальної комірки для помітки кольором в екранній
таблиці:}
Self. CurGridSolveCol:=CurColNum+HeadColNum+bc_LTaskColsBeforeVars;
Self. CurGridSolveRow:=CurRowNum+HeadRowNum+bc_LTaskRowsBeforeVars;
{Перевіряємо, чи не є поточна комірка нулем, і при потребі шукаємо
ненульову:}
If Not (Self. SearchNozeroSolveCell (CurRowNum, CurColNum,
Length (Self. CurHeadCol) – 1, Length (Self. CurHeadRow) – 1,
HeadRowNum, HeadColNum)) then
Break; {якщо не знайдено…}
If Self. Stop then Goto LStopLabel;
WaitForNewStep (HeadColNum, HeadRowNum);
{Якщо дано команду перервати розв'язування:}
If Self. Stop then Goto LStopLabel;
ColDeleted:=False;
{Обробляємо таблицю звичайним Жордановим виключенням:}
If Not (Self.GI (CurColNum, CurRowNum, Self. CurHeadRow, Self. CurHeadCol,
Self. CurTable, ColDeleted, False, True)) then
Begin
SolveEqsWithM1:=False;
Exit;
End;
{Переходимо до наступного рядка, так як у цьому вже виразили одну із
змінних:}
Inc(CurRowNum);
If Not(ColDeleted) then Inc(CurColNum);
End;
ShowResultCalc;
SolveEqsWithM1:=True;
Exit;
LStopLabel:
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_SolvingStopped);
SolveEqsWithM1:=False;
Exit;
End;
{Вирішування системи лінійних рівнянь способом 2:}
Function TGridFormattingProcs. SolveEqsWithM2: Boolean;
{Для таблиці виду:
x1 x2 x3… xn 1
0
0
0
…
0}
Const sc_CurProcName='SolveEqsWithM2';
Var CurRowNum, CurColNum: Integer;
st1: String;
HeadRowNum, HeadColNum: Integer;
ColDeleted: Boolean;
ProcedureShowResultCalc;
{Відображає записи значень змінних (у текстовому полі)
такого зказка:
<стовп1>=<значення1>;
…
<стовп
m
>=<значення
m
>;
та відображає повідомлення про наявність коренів і їх визначеність.}
Var CurRowN, CurColN: Integer;
CurVal:TWorkFloat;
NotEqual, NoRoots, FreeRoots: Boolean;
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_ResultIs+sc_DoubleSpot);
NoRoots:=False;
For CurRowN:=0 to Length (Self. CurHeadCol) – 1 do
Begin
If Self. CurOutConsole<>Nil then
Begin
st1:='';
{<стовп i>=…:}
If Self. CurHeadCol[CurRowN].ElmType=bc_Number then
st1:=st1+FloatToStr (Self. CurHeadCol[CurRowN].AsNumber)
Else
st1:=st1+Self. CurHeadCol[CurRowN].AsVarName;
End;
NotEqual:=False;
CurVal:=Self. CurTable [CurRowN, Length (Self. CurHeadRow) – 1];
If Self. CurHeadCol[CurRowN].ElmType=bc_Number then
Begin
If Self. CurHeadCol[CurRowN].AsNumber<>CurVal then
Begin NoRoots:=True; NotEqual:=True; End;
End;
If Self. CurOutConsole<>Nil then
Begin
If NotEqual then
st1:=st1+sc_Space+sc_NotEqual+sc_Space {<>}
Else st1:=st1+sc_Space+sc_Equal+sc_Space; {=}
st1:=st1+FloatToStr(CurVal)+sc_KrKm; {<стовп i><V><значення>;}
Self. CurOutConsole. Lines. Add(st1);
End;
End; {For CurRowN:=0 to Length (Self. CurHeadCol) – 1 do…}
{Переріряємо, чи залишилися змінні у рядку-заголовку.
Якщо так, то
корені вільні, і якщо система сумісна, то їх безліч:}
FreeRoots:=False;
For CurColN:=0 to Length (Self. CurHeadRow) – 1 do
Begin
If Self. CurHeadRow[CurColN].ElmType<>bc_Number then
Begin FreeRoots:=True; Break; End;
End;
If NoRoots then
Begin
If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_NoRoots);
Self. WasNoRoots:=True;
End
Else if FreeRoots then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_ManyRoots);
Self. WasManyRoots:=True;
End
Else
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_SolutionFound);
Self. SolWasFound:=True;
End;
End;
Label LStopLabel;
Begin
If Self. TaskWidth<=0 then{Якщо таблиця пуста, то задача пуста:}
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_EmptyTable);
SolveEqsWithM2:=False;
Exit;
End;
HeadRowNum:=Self.CHeadRowNum;
HeadColNum:=Self.CHeadColNum;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_StartSolving);
CurRowNum:=0; {починаємо з першого рядка}
{Проходимо по усіх стовпцях (по усіх змінних), намагаючись брати
розв'язувальні комірки по головній діагоналі. Якщо серед таких зустрінеться
нуль, спробуємо знайти ненульову комірку нижче, і поміняти рядки нульової
з ненульовою, щоб ненульова стала на головній діагоналі.
При цьому останній стовпець не беремо (у ньому вільні члени –
праві частини рівнянь):}
CurColNum:=0;
While (CurColNum<(Length (Self. CurHeadRow) – 1)) and{останній стовпець не беремо}
(CurRowNum<Length (Self. CurHeadCol)) do
Begin
{Координати розв'язувальної комірки для помітки кольором в екранній
таблиці:}
Self. CurGridSolveCol:=CurColNum+HeadColNum+bc_LTaskColsBeforeVars;
Self. CurGridSolveRow:=CurRowNum+HeadRowNum+bc_LTaskRowsBeforeVars;
{Перевіряємо, чи не є поточна комірка нулем, і при потребі шукаємо
ненульову серед коефіцієнтів, окрім стовпця вільних членів
(що є останнім):}
If Not (Self. SearchNozeroSolveCell (CurRowNum, CurColNum,
Length (Self. CurHeadCol) – 1, Length (Self. CurHeadRow) – 2,
HeadRowNum, HeadColNum)) then
Break; {якщо не знайдено…}
If Self. Stop then Goto LStopLabel;
WaitForNewStep (HeadColNum, HeadRowNum);
{Якщо дано команду перервати розв'язування:}
If Self. Stop then Goto LStopLabel;
ColDeleted:=False;
{Обробляємо таблицю звичайним Жордановим виключенням:}
If Not (Self.GI (CurColNum, CurRowNum, Self. CurHeadRow, Self. CurHeadCol,
Self. CurTable, ColDeleted, False, True)) then
Begin
SolveEqsWithM2:=False;
Exit;
End;
{Переходимо до наступного рядка, так як у цьому вже виразили одну із
змінних:}
Inc(CurRowNum);
If Not(ColDeleted) then Inc(CurColNum);
End;
ShowResultCalc;
SolveEqsWithM2:=True;
Exit;
LStopLabel:
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_SolvingStopped);
SolveEqsWithM2:=False;
Exit;
End;
{Запускач вирішування. Працює у режимах fs_SolvingEqsM1,
fs_SolvingEqsM2, fs_SolvingLTask:}
Function TGridFormattingProcs. Solve (ToGoToEnd: Boolean=False):Boolean;
Const sc_CurProcName='Solve';
Var
Res1: Boolean;
st1: String;
Begin
Self. InSolving:=True;
Self. WasNoRoots:=False; Self. WasManyRoots:=False; Self. SolWasFound:=False;
Self. Stop:=False; Self. GoToEnd:=ToGoToEnd;
Res1:=False;
Case Self. CurFormatState of
fs_SolvingEqsM1: Res1:=Self. SolveEqsWithM1;
fs_SolvingEqsM2: Res1:=Self. SolveEqsWithM2;
fs_SolvingLTask: Res1:=Self. SolveMultiCritLTask;
Else
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_NoSolveMode);
End;
End;
If Self. CurOutConsole<>Nil then
Begin
st1:='Вирішування закінчено.';
If Res1 then st1:=st1+' Успішно.' else st1:=st1+' З помилками' + sc_TriSpot;
Self. CurOutConsole. Lines. Add(st1);
End;
Self. InSolving:=False;
{Відображаємо таблицю вкінці вирішування:}
Self. WriteTableToGrid (Self.CHeadColNum, Self.CHeadRowNum, True);
Solve:=Res1;
End;
Constructor TGridFormattingProcs. Create;
Begin
Inherited Create;
InSolving:=False;
SolWasFound:=False; WasNoRoots:=False; WasManyRoots:=False;
EqM1TaskPrepared:=False; EqM2TaskPrepared:=False; LTaskPrepared:=False;
Continue:=False; GoToEnd:=False; Stop:=False;
CurGridModified:=False;
CurGridSolveCol:=0; CurGridSolveRow:=0;
TableFormatState:=fs_NoFormatting;
StringGrid:=Nil;
OldOnNewCol:=Nil;
OldOnNewRow:=Nil;
OldOnDrawCell:=Nil;
OldOnDblClick:=Nil;
OldOnMouseUp:=Nil;
OldOnSetEditText:=Nil;
{SetLength (CurHeadRow, 0); SetLength (CurHeadCol, 0);
SetLength (CurTable, 0);}
Self. CurHeadRow:=Nil;
Self. CurHeadCol:=Nil;
Self. CurTable:=Nil;
Self. CopyHeadRow:=Nil;
Self. CopyHeadCol:=Nil;
Self. CopyTable:=Nil;
CurOutConsole:=Nil;
End;
Destructor TGridFormattingProcs. Free;
Begin
{Inherited Free;}
{inaccessible value;
…raised too many consecutive exceptions:
access violation at address 0x00000000 read of address 0x00000000…}
End;
Function TGridFormattingProcs. GetColorByElmType (CurType:THeadLineElmType):TColor;
Const sc_CurProcName='GetColorByElmType';
Var CurColor:TColor;
Begin
Case CurType of
bc_IndependentVar: CurColor:=lwc_IndependentColor;
bc_DependentVar: CurColor:=lwc_DependentColor;
bc_FuncVal: CurColor:=lwc_HeadColColor;
bc_Number: CurColor:=lwc_ValInHeadColOrRowColor;
bc_DestFuncToMax: CurColor:=lwc_DestFuncToMaxNameColor;
bc_DestFuncToMin: CurColor:=lwc_DestFuncToMinNameColor;
bc_OtherType:
If Self. CurGrid<>Nil then CurColor:=Self. CurGrid. Color
else CurColor:=clWindow;
Else
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+':'+sc_Space+
sc_UnknownVarType+sc_Space+IntToStr (Ord(CurType))+
sc_Space+sc_TriSpot);
CurColor:=bc_NotColored;
End;
End;
GetColorByElmType:=CurColor;
End;
Function TGridFormattingProcs. GetNameByElmType (CurType:THeadLineElmType):String;
Const sc_CurProcName='GetNameByElmType';
Var CurName: String;
Begin
Case CurType of
bc_IndependentVar: CurName:=sc_IndependentVar;
bc_DependentVar: CurName:=sc_DependentVar;
bc_FuncVal: CurName:=sc_InequalFuncName;
bc_Number: CurName:=sc_ValInHeadColOrRow;
bc_DestFuncToMax: CurName:=sc_DestFuncToMaxName;
bc_DestFuncToMin: CurName:=sc_DestFuncToMinName;
bc_OtherType: CurName:=sc_OtherType;
Else
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+':'+sc_Space+
sc_UnknownVarType+sc_Space+IntToStr (Ord(CurType))+sc_Space+
sc_TriSpot);
CurName:=sc_UnknownVarType;
End;
End;
GetNameByElmType:=CurName;
End;
Function TGridFormattingProcs. ReadFromFile (Const SPath: String):Boolean;
{Читання умови задачі із файла.}
Const sc_CurProcName='ReadFromFile';
Var CurFile: File; CurColCount, CurRowCount, CurCol, CurRow, ControlSize: Integer;
GotFormatState:TTableFormatState;
CurMessage: String;
Begin
If ((Self. CurFormatState<>fs_EnteringEqs) and
(Self. CurFormatState<>fs_EnteringLTask) and
(Self. CurFormatState<>fs_NoFormatting) and
(Self. CurFormatState<>fs_FreeEdit))
or (Self. InSolving) then
Begin
CurMessage:=sc_CurProcName+sc_CantReadTaskInCurMode+sc_TriSpot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
ReadFromFile:=False; Exit;
End;
System. AssignFile (CurFile, SPath);
System. FileMode:=fmOpenRead;
try {Пробуємо відкрити файл:}
System. Reset (CurFile, 1);
except
CurMessage:=sc_CurProcName+sc_CantOpenFile+SPath+sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
ReadFromFile:=False; Exit;
End;
try {Пробуємо прочитати дескриптори кількості рядків і стовпців у задачі:}
System. BlockRead (CurFile, CurColCount, SizeOf(CurColCount));
System. BlockRead (CurFile, CurRowCount, SizeOf(CurRowCount));
Except
CurMessage:=sc_CurProcName+sc_EmptyFileOrCantRead+SPath+
sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
ReadFromFile:=False; Exit;
End;
{Обчислюємо розмір, який повинні займати усі дані у файлі:}
ControlSize:=SizeOf(CurColCount)+SizeOf(CurRowCount)+
+SizeOf (Self. CurFormatState)+
SizeOf(TValOrName)*CurColCount+ SizeOf(TValOrName)*CurRowCount+
SizeOf(TWorkFloat)*CurColCount*CurRowCount;
{Перевіряємо, чи має файл такий розмір:}
If ControlSize<>System. FileSize(CurFile) then
Begin
CurMessage:=sc_CurProcName+sc_FileNotFullOrHasWrongFormat+SPath+
sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
ReadFromFile:=False; Exit;
End;
Try
System. BlockRead (CurFile, GotFormatState, SizeOf(GotFormatState));
Except
CurMessage:=sc_CurProcName+sc_CantReadFile+SPath+sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
ReadFromFile:=False; Exit;
End;
{Встановлюємо режим, що був збережений у файлі разом з умовою задачі:}
Self. TableFormatState:=GotFormatState;
{Читаємо рядок-заголовок:}
SetLength (Self. CurHeadRow, CurColCount);
For CurCol:=0 to CurColCount-1 do
Begin
Try
System. BlockRead (CurFile, Self. CurHeadRow[CurCol], SizeOf(TValOrName));
Except
CurMessage:=sc_CurProcName+sc_CantReadFile+SPath+sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
ReadFromFile:=False; Exit;
End;
End;
{Читаємо стовпець-заголовок:}
SetLength (Self. CurHeadCol, CurRowCount);
For CurRow:=0 to CurRowCount-1 do
Begin
Try
System. BlockRead (CurFile, Self. CurHeadCol[CurRow], SizeOf(TValOrName));
Except
CurMessage:=sc_CurProcName+sc_CantReadFile+SPath+sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
ReadFromFile:=False; Exit;
End;
End;
{Читаємо таблицю коефіцієнтів і вільних членів:}
SetLength (Self. CurTable, CurRowCount, CurColCount);
For CurRow:=0 to CurRowCount-1 do
Begin
For CurCol:=0 to CurColCount-1 do
Begin
Try
System. BlockRead (CurFile, Self. CurTable [CurRow, CurCol],
SizeOf(TWorkFloat));
Except
CurMessage:=sc_CurProcName+sc_CantReadFile+SPath+sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
ReadFromFile:=False; Exit;
End;
End;
End;
Try
System. Close(CurFile);
Except
CurMessage:=sc_CurProcName + sc_CantCloseFile + SPath + sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
End;
Self. CurGridModified:=False;
Self. Refresh;
{Відмічаємо, що прочитана умова задачі не підготована ще до вирішування
жодним із методів вирішування:}
Self. EqM1TaskPrepared:=False;
Self. EqM2TaskPrepared:=False;
Self.LTaskPrepared:=False;
ReadFromFile:=True;
End;
Function TGridFormattingProcs. SaveToFile (Const SPath: String):Boolean;
{Запис умови задачі у файл.}
Const sc_CurProcName='SaveToFile';
Var CurFile: File; CurColCount, CurRowCount, CurCol, CurRow: Integer;
CurMessage: String;
Begin
If ((Self. CurFormatState<>fs_EnteringEqs) and
(Self. CurFormatState<>fs_EnteringLTask) and
(Self. CurFormatState<>fs_FreeEdit))
or (Self. InSolving) then
Begin
CurMessage:=sc_CurProcName+sc_CantWriteTaskInCurMode;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
SaveToFile:=False; Exit;
End;
{Якщо таблиця модифікована, умова не прочитана з неї, то читаємо:}
If Self. CurGridModified then
Begin
If Not (Self. GetTask(True)) then
Begin
SaveToFile:=False; Exit;
End;
End;
System. AssignFile (CurFile, SPath);
System. FileMode:=fmOpenWrite;
try {Пробуємо створити новий файл:}
System. Rewrite (CurFile, 1);
except
CurMessage:=sc_CurProcName+sc_CantCreateFile+SPath+sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
SaveToFile:=False; Exit;
End;
Self. GetTaskSizes (CurColCount, CurRowCount);
try {Пробуємо прочитати дескриптори кількості рядків і стовпців у задачі:}
System. BlockWrite (CurFile, CurColCount, SizeOf(CurColCount));
System. BlockWrite (CurFile, CurRowCount, SizeOf(CurRowCount));
System. BlockWrite (CurFile, Self. CurFormatState,
SizeOf (Self. CurFormatState));
Except
CurMessage:=sc_CurProcName+sc_CantWriteFile+SPath+sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
SaveToFile:=False; Exit;
End;
{Записуємо рядок-заголовок:}
For CurCol:=0 to CurColCount-1 do
Begin
Try
System. BlockWrite (CurFile, Self. CurHeadRow[CurCol], SizeOf(TValOrName));
Except
CurMessage:=sc_CurProcName+sc_CantWriteFile+SPath+sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
SaveToFile:=False; Exit;
End;
End;
{Записуємо стовпець-заголовок:}
For CurRow:=0 to CurRowCount-1 do
Begin
Try
System. BlockWrite (CurFile, Self. CurHeadCol[CurRow], SizeOf(TValOrName));
Except
CurMessage:=sc_CurProcName+sc_CantWriteFile+SPath+sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
SaveToFile:=False; Exit;
End;
End;
{Записуємо таблицю коефіцієнтів і вільних членів:}
For CurRow:=0 to CurRowCount-1 do
Begin
For CurCol:=0 to CurColCount-1 do
Begin
Try
System. BlockWrite (CurFile, Self. CurTable [CurRow, CurCol],
SizeOf(TWorkFloat));
Except
CurMessage:=sc_CurProcName+sc_CantWriteFile+SPath+sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
SaveToFile:=False; Exit;
End;
End;
End;
Try
System. Close(CurFile);
Except
CurMessage:=sc_CurProcName + sc_CantCloseFile + SPath + sc_DoubleQuot;
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add(CurMessage);
MessageDlg (CurMessage, mtError, [mbOk], 0);
SaveToFile:=False; Exit;
End;
SaveToFile:=True;
End;
Procedure TGridFormattingProcs. SetTable (Const SHeadRow, SHeadCol:TValOrNameMas;
Const STable:TFloatMatrix);
{Задає нову таблицю і загноловки (що могли бути сформовані поза об'єктом):}
Begin
Self. CurTable:=STable;
Self. CurHeadRow:=SHeadRow;
Self. CurHeadCol:=SHeadCol;
Self. TaskWidth; {перевіряємо розміри нової таблиці і її заголовків}
End;
Procedure TGridFormattingProcs. GetTable (Var DHeadRow, DHeadCol:TValOrNameMas;
Var DTable:TFloatMatrix);
{Повертає посилання на таблицю і її заголовки.}
Begin
DTable:=Self. CurTable;
DHeadRow:=Self. CurHeadRow;
DHeadCol:=Self. CurHeadCol;
End;
Procedure TGridFormattingProcs. ReadHeadRowCell (SCol: Integer);
{Зчитує комірку з екранної таблиці в рядок-заголовок.
Вхідні дані:
SCol
– номер комірки у рядку-заголовку.
Для екранної таблиці використовуються координати комірки відповідно до
координат рядка-заголовка та стовпця заголовка (верхнього лівого кута
таблиці з заголовками):
HeadColNumInGrid
і
HeadRowNumInGrid
.}
Var CurFloatVal:TWorkFloat; CurElmType:THeadLineElmType;
Begin
CurElmType:=CurHeadRow[SCol].ElmType;
CurFloatVal:=0;
Try {Пробуємо розпізнати число:}
CurFloatVal:=StrToFloat (CurGrid. Cells [SCol+bc_LTaskColsBeforeVars+
Self.CHeadColNum, Self.CHeadRowNum]);
CurElmType:=bc_Number; {якщо число розпізналося, то це число}
Except{Якщо рядок не інтерпретується як число, але під час редагування
була зроблена помітка про те, що це є число або функція, то вважаємо
його назвою незалежної змінної (бо всі функції в умові задачі мають
бути в стовпці-заголовку, а не в рядку):}
If (CurElmType<>bc_IndependentVar) and (CurElmType<>bc_DependentVar) then
CurElmType:=bc_IndependentVar;
End; {Виправлений тип елемента:}
CurHeadRow[SCol].ElmType:=CurElmType;
If CurElmType=bc_Number then {записуємо число, якщо розпізналося:}
CurHeadRow[SCol].AsNumber:=CurFloatVal
Else
Begin {якщо число не розпізналося, то записуємо як назву змінної:}
With CurHeadRow[SCol] do
Begin
AsVarName:=CurGrid. Cells [SCol+bc_LTaskColsBeforeVars+Self.CHeadColNum,
Self.CHeadRowNum]; {назва}
VarInitPos:=SCol; {номер п/п у рядку в умові задачі}
VarInitInRow:=True; {ознака, що змінна спочатку була у рядку-заголовку}
End;
End;
End;
Procedure TGridFormattingProcs. ReadHeadColCell (SRow: Integer);
{Зчитує комірку з екранної таблиці в стовпець-заголовок.
Вхідні дані:
SRow
– номер комірки у стовпці-заголовку.
Для екранної таблиці використовуються координати комірки відповідно до
координат рядка-заголовка та стовпця заголовка (верхнього лівого кута
таблиці з заголовками):
HeadColNumInGrid
і
HeadRowNumInGrid
.}
Var CurFloatVal:TWorkFloat; CurElmType:THeadLineElmType;
Begin
CurElmType:=CurHeadCol[SRow].ElmType;
CurFloatVal:=0;
Try {Пробуємо розпізнати число:}
CurFloatVal:=StrToFloat (CurGrid. Cells [Self.CHeadColNum,
SRow+bc_LTaskRowsBeforeVars+Self.CHeadRowNum]);
CurElmType:=bc_Number; {якщо число розпізналося, то це число}
Except{Якщо рядок не інтерпретується як число, але комірка вважалася
такою, що містить число або змінну, то вважаємо його назвою функції
(бо це не число, і не повинно бути змінною – усі змінні спочатку
у рядку-заголовку):}
If (CurElmType<>bc_FuncVal) and (CurElmType<>bc_DestFuncToMax) and
(CurElmType<>bc_DestFuncToMin) then
CurElmType:=bc_FuncVal;
End; {Виправлений тип елемента:}
CurHeadCol[SRow].ElmType:=CurElmType;
If CurElmType=bc_Number then {записуємо число, якщо розпізналося:}
CurHeadCol[SRow].AsNumber:=CurFloatVal
Else
Begin {якщо число не розпізналося, то записуємо як назву змінної:}
With CurHeadCol[SRow] do
Begin
AsVarName:=CurGrid. Cells [Self.CHeadColNum,
SRow+bc_LTaskRowsBeforeVars+Self.CHeadRowNum]; {назва}
VarInitPos:=SRow; {номер п/п у стовпці в умові задачі}
{Ознака, що змінна спочатку була у стовпці-заголовку:}
VarInitInRow:=False;
End;
End;
End;
Function TGridFormattingProcs. ReadTableFromGrid: Boolean;
Const sc_CurProcName='ReadTableFromGrid';
{Процедура для зчитування таблиці та її заголовків із CurGrid.
Для екранної таблиці використовуються координати рядка-заголовка та
стовпця заголовка (верхнього лівого кута таблиці з заголовками):
HeadColNumInGrid (CHeadColNum) і HeadRowNumInGrid (CHeadRowNum).}
Var CurRow, CurCol, CurWidth, CurHeight: Integer;
CurFloatVal:TWorkFloat;
Begin
If Self. CurGrid=Nil then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+
': '+sc_NoGrowingStringGrid);
ReadTableFromGrid:=False;
Exit;
End;
{Ширина і висота таблиці з заголовками:}
CurWidth:=Self. CurGrid. ColCount-Self.CHeadColNum-bc_LTaskColsBeforeVars;
CurHeight:=Self. CurGrid. RowCount-Self.CHeadRowNum-bc_LTaskRowsBeforeVars;
If (CurHeight<=0) or (CurWidth<=0) then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+
': починаючи з комірки ['+IntToStr (Self.CHeadColNum+1)+'; '+
IntToStr (Self.CHeadRowNum+1)+'] таблиці не знайдено' + sc_TriSpot);
ReadTableFromGrid:=False;
Exit;
End;
{Виділяємо пам'ять:}
SetLength (Self. CurHeadRow, CurWidth); {рядок-заголовок}
SetLength (Self. CurHeadCol, CurHeight); {стовпець-заголовок}
SetLength (Self. CurTable, CurHeight, CurWidth); {таблиця}
{Читаємо рядок-заголовок:}
For CurCol:=0 to CurWidth-1 do ReadHeadRowCell(CurCol);
{Читаємо стовпець-заголовок:}
For CurRow:=0 to CurHeight-1 do ReadHeadColCell(CurRow);
{Читаємо таблицю коефіцієнтів:}
For CurRow:=Self.CHeadRowNum+bc_LTaskRowsBeforeVars to
Self. CurGrid. RowCount-1 do
Begin
For CurCol:=Self.CHeadColNum+bc_LTaskColsBeforeVars to
Self. CurGrid. ColCount-1 do
Begin
Try {Пробуємо інтерпретувати рядок із комірки як число:}
CurFloatVal:=StrToFloat (CurGrid. Cells [CurCol, CurRow]);
Except{Якщо не вдалося, то вважаємо це число нулем:}
CurFloatVal:=0;
End;
Self. CurTable [CurRow-bc_LTaskRowsBeforeVars-Self.CHeadRowNum,
CurCol-bc_LTaskColsBeforeVars-Self.CHeadColNum]:=CurFloatVal;
End;
End;
{Після читання зміни в екранній таблиці враховані:}
Self. CurGridModified:=False;
ReadTableFromGrid:=True;
End;
Function TGridFormattingProcs. WriteTableToGrid (SHeadColNum,
SHeadRowNum: Integer; ToTuneColWidth: Boolean=True):Boolean;
{Процедура для відображення таблиці та її заголовків у
CurGrid
.}
Const sc_CurProcName='WriteTableToGrid';
Var CurRow, CurCol, CurWidth, CurHeight: Integer;
CurElmType:THeadLineElmType;
Begin
If Self. CurGrid=Nil then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+
': GrowingStringGrid не заданий!..');
WriteTableToGrid:=True;
Exit;
End;
{Ширина і висота таблиці:}
Self. GetTaskSizes (CurWidth, CurHeight);
If (CurHeight<=0) or (CurWidth<=0) then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_EmptyTable);
WriteTableToGrid:=False;
Exit;
End;
{Виділяємо комірки для таблиці у екранному CurGrid:}
Self. CurGrid. ColCount:=CurWidth+SHeadColNum+1;
Self. CurGrid. RowCount:=CurHeight+SHeadRowNum+1;
{Відображаємо рядок-заголовок:}
For CurCol:=SHeadColNum+1 to Self. CurGrid. ColCount-1 do
Begin
CurElmType:=CurHeadRow [CurCol-1-SHeadColNum].ElmType;
If CurElmType=bc_Number then {записуємо число, якщо є числом:}
CurGrid. Cells [CurCol, SHeadRowNum]:=
FloatToStr (CurHeadRow[CurCol-1-SHeadColNum].AsNumber)
Else{Якщо це не число, то це рядок з якоюсь назвою.
Записуємо:}
Self. CurGrid. Cells [CurCol, SHeadRowNum]:=
CurHeadRow [CurCol-1-SHeadColNum].AsVarName;
End;
{Відображаємо стовпець-заголовок:}
For CurRow:=SHeadRowNum+1 to Self. CurGrid. RowCount-1 do
Begin
CurElmType:=CurHeadCol [CurRow-1-SHeadRowNum].ElmType;
If CurElmType=bc_Number then {записуємо число, якщо є числом:}
CurGrid. Cells [SHeadColNum, CurRow]:=
FloatToStr (CurHeadCol[CurRow-1-SHeadRowNum].AsNumber)
Else{Якщо це не число, то це рядок з якоюсь назвою.
Записуємо:}
Self. CurGrid. Cells [SHeadColNum, CurRow]:=
CurHeadCol [CurRow-1-SHeadRowNum].AsVarName;
End;
{Відображаємо таблицю коефіцієнтів:}
For CurRow:=SHeadRowNum+1 to Self. CurGrid. RowCount-1 do
Begin
For CurCol:=SHeadColNum+1 to Self. CurGrid. ColCount-1 do
CurGrid. Cells [CurCol, CurRow]:=
FloatToStr (Self. CurTable [CurRow-1-SHeadRowNum, CurCol-1-SHeadColNum]);
End;
{Комірка на перехресті заголовків пуста:}
If (SHeadRowNum<Self. CurGrid. RowCount) and
(SHeadColNum<Self. CurGrid. ColCount) then
CurGrid. Cells [SHeadColNum, SHeadRowNum]:='';
{Після запису в екранну таблицю: зміни, що могли бути у ній, вважаємо
затертими:}
Self. CurGridModified:=False;
{Якщо задано, настроюємо ширини стовпців по довжині тексту у комірках:}
If ToTuneColWidth then Self. CurGrid. TuneColWidth;
WriteTableToGrid:=True;
End;
Procedure TGridFormattingProcs. GetTaskSizes (Var DWidth, DHeight: Integer);
{Визначення розмірів таблиці задачі, і корегування довжини заголовків
таблиці та зовнішнього масиву таблиці (масиву масивів).}
Begin
DHeight:=Length (Self. CurTable);
If DHeight>0 then
DWidth:=Length (Self. CurTable[0])
Else DWidth:=0;
If DWidth=0 then DHeight:=0;
If DWidth>Length (Self. CurHeadRow) then
DWidth:=Length (Self. CurHeadRow);
If DHeight>Length (Self. CurHeadCol) then
DHeight:=Length (Self. CurHeadCol);
{Якщо комірок немає, то:}
If DWidth=0 then
Begin
{Зовнійшій масив встановлюємо у нульову довжину:}
SetLength (Self. CurTable, 0);
{Заголовки теж:}
SetLength (Self. CurHeadRow, 0);
SetLength (Self. CurHeadCol, 0);
End;
End;
{Розміри прочитаної таблиці задачі:}
Function TGridFormattingProcs. TaskWidth: Integer;
Var CurWidth, CurHeight: Integer;
Begin
Self. GetTaskSizes (CurWidth, CurHeight);
TaskWidth:=CurWidth;
End;
Function TGridFormattingProcs. TaskHeight: Integer;
Var CurWidth, CurHeight: Integer;
Begin
Self. GetTaskSizes (CurWidth, CurHeight);
TaskHeight:=CurHeight;
End;
Function TGridFormattingProcs. GetTask (ToPrepareGrid: Boolean=True):Boolean;
{Зчитування умови задачі із
CurGrid
та відображення прочитаного
на тому ж місці, де воно було.
Працює у режимах
fs_EnteringEqs і fs_EnteringLTask.}
Const sc_CurProcName='GetTask';
Var Res1: Boolean;
Procedure DoGetTask;
Begin
If ToPrepareGrid then
CurGrid. ShrinkToFilled (Self.CHeadColNum+1, Self.CHeadRowNum+1);
{Читаємо комірки таблиці:}
Res1:=Self. ReadTableFromGrid;
{Відображаємо те, що вийшло прочитати, у тих самих комірках на екрані:}
If Not (Self. WriteTableToGrid (Self.CHeadColNum, Self.CHeadRowNum)) then
Res1:=False;
End;
Begin
If Self. CurGrid=Nil then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+': '+sc_NoGrowingStringGrid);
GetTask:=False;
Exit;
End;
Case Self. CurFormatState of
fs_EnteringEqs: {режим редагування системи лінійних рівнянь:}
Begin
{Зчитуємо таблицю. Як рядок-заголовок зчитуємо автоматично
сформовані назви змінних
x
1…
xn
та множник вільних членів (1).
Як стовпець-заголовок зчитуємо стовпець нумерації.
При переході до режиму вирішування задачі у цей стовпець
будуть скопійовані вільні члени (режим способу 1,
fs
_
SolvingEqsM
1),
або нулі (режим способу 2,
fs
_
SolvingEqsM
2):}
DoGetTask;
If Not(Res1) then Begin GetTask:=False; Exit; End;
End;
fs_EnteringLTask: {режим редагування форми задачі лінійного програмування:}
Begin
{Зчитуємо таблицю умови для задачі ЛП максимізації або
мінімізації лінійної форми (функції з умовами-нерівностями,
рівняннями та обмеженнями невід'ємності, імена змінних, нерівностей,
функцій):}
DoGetTask;
If Not(Res1) then Begin GetTask:=False; Exit; End;
End;
fs_FreeEdit: {режим вільного редагування:}
Begin
{Читаємо таблицю, рядок-заголовок, стовпець-заголовок:}
DoGetTask;
If Not(Res1) then Begin GetTask:=False; Exit; End;
End;
Else {інші режими:}
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_CantReadTaskInCurMode
+ sc_TriSpot);
GetTask:=False;
Exit;
End;
End;
{If ToPrepareGrid then CurGrid. TuneColWidth;}
Self. EqM1TaskPrepared:=False;
Self. EqM2TaskPrepared:=False;
Self.LTaskPrepared:=False;
GetTask:=True;
End;
Procedure TGridFormattingProcs. Refresh;
Const sc_CurProcName='Refresh';
Var Res1: Boolean;
Begin
If Self. CurFormatState<>fs_NoFormatting then
Begin
If Self. CurGrid=Nil then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+': '+
sc_NoGrowingStringGrid);
Exit;
End;
Res1:=False;
{Якщо таблиця редагована або ще не читана, то запускаємо її зчитування:}
If Self. CurGridModified or (Self. TaskWidth<=0) then Res1:=Self. GetTask;
If Not(Res1) then {Якщо таблиця не була віджображена у GetTask, відображаємо:}
Self. WriteTableToGrid (Self.CHeadColNum, Self.CHeadRowNum);
End;
End;
Procedure TGridFormattingProcs. ResetModified; {скидає прапорець зміненого стану}
Begin
Self. CurGridModified:=False;
End;
Procedure TGridFormattingProcs. UndoChanges;
{Відкидає останні зміни (ResetModified+Refresh).}
Begin
Self. ResetModified; Self. Refresh;
End;
Procedure Transpose (Var SDMatrix:TFloatMatrix);
{Транспонування двовимірної матриці.}
Var CurCol, CurRow, CurWidth, CurHeight: Integer;
SafeElm:TWorkFloat;
Begin
CurHeight:=Length(SDMatrix);
If CurHeight>0 then CurWidth:=Length (SDMatrix[0])
Else CurWidth:=0;
If (CurHeight=0) or (CurWidth=0) then Exit;
{Збільшуємо розміри матриці до квадратних:}
IfCurWidth>CurHeightthen{Якщо ширина була більша за висоту:}
Begin
SetLength (SDMatrix, CurWidth, CurWidth); {збільшуємо висоту}
End
ElseifCurWidth<CurHeightthen{Якщо висота була більша за ширину:}
Begin
SetLength (SDMatrix, CurHeight, CurHeight); {збільшуємо ширину}
End;
{Міняємо елементи місцями: рядки будуть стовпцями, а стовпці – рядками:}
For CurRow:=0 to Length(SDMatrix) – 1 do
Begin
For CurCol:=CurRow + 1 to Length (SDMatrix[CurRow]) – 1 do
Begin
SafeElm:=SDMatrix [CurRow, CurCol];
SDMatrix [CurRow, CurCol]:=SDMatrix [CurCol, CurRow];
SDMatrix [CurCol, CurRow]:=SafeElm;
End;
End;
{Ширина тепер буде така як була висота, а висота – як була ширина:}
SetLength (SDMatrix, CurWidth, CurHeight);
End;
Function TGridFormattingProcs. MakeDualLTask: Boolean;
{Перехід від зчитаної умови задачі максимізації чи мінімізації
лінійної форми до двоїстої задачі.
Працює у режимі редагування
задачі максимізації-мінімізації (fs_EnteringLTask).
За правилом двоїсту задачу потрібно мінімізувати, якщо для прямої
потрібно було знайти максимум, і максимізувати, якщо для прямої потрібно
було знайти мінімум.
}
Constsc_CurProcName='MakeDualLTask';
Var SafeMas:TValOrNameMas; CurCol, CurRow, DFuncCount: Integer;
DualTType:TDualTaskType; NewDFuncType, OldDFuncType:THeadLineElmType;
Begin
SafeMas:=Nil;
If Self. CurFormatState<>fs_EnteringLTask then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_CanMakeOnlyInELTaskMode);
MakeDualLTask:=False; Exit;
End;
If Self. CurGridModified then
Begin
If Not (Self. GetTask(True)) then
Begin
MakeDualLTask:=False; Exit;
End;
End;
If Self. TaskHeight<=0 then {Якщо таблиця пуста:}
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_EmptyTable);
MakeDualLTask:=False; Exit;
End;
{Перевіряємо, чи функція мети лише одна, і визначаємо її тип
(для максимізації чи мінімізації):}
DFuncCount:=0; DualTType:=dt_MaxToMin; OldDFuncType:=bc_DestFuncToMax;
For CurRow:=0 to Length (Self. CurHeadCol) – 1 do
Begin
If Self. CurHeadCol[CurRow].ElmType=bc_DestFuncToMax then
Begin
DualTType:=dt_MaxToMin;
OldDFuncType:=Self. CurHeadCol[CurRow].ElmType;
Inc(DFuncCount);
End
Else if Self. CurHeadCol[CurRow].ElmType=bc_DestFuncToMin then
Begin
DualTType:=dt_MinToMax;
OldDFuncType:=Self. CurHeadCol[CurRow].ElmType;
Inc(DFuncCount);
End;
End;
{Якщо функцій мети декілька або жодної:}
If DFuncCount<>1 then
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+
sc_CanMakeDTaskOnlyForOneDFunc+IntToStr(DFuncCount));
MakeDualLTask:=False; Exit;
End;
If DualTType=dt_MaxToMin then NewDFuncType:=bc_DestFuncToMin
Else NewDFuncType:=bc_DestFuncToMax;
{Зсуваємо рядок функції мети вниз таблиці.
При цьому позначки порядку
рядків залишаємо на тих самих місцях (і присвоюємо тим рядкам, які
стають на ці місця):}
Self. ShiftRowsDown([bc_DestFuncToMax, bc_DestFuncToMin], True);
Transpose (Self. CurTable); {транспонуємо таблицю коефіцієнтів}
{Обробляємо заголовки таблиці у відповідність до двоїстої задачі:}
{Для рядка-заголовка, що стане стовпцем-заголовком:}
For CurCol:=0 to Length (Self. CurHeadRow) – 1 do
Begin {Проходимо по усіх змінних і останньому елементу –
множнику стовпця вільних членів – одиниці:}
If Self. CurHeadRow[CurCol].ElmType=bc_DependentVar then {Якщо змінна >=0:}
Begin {Ця комірка буде заголовком функції умови-нерівності зі знаком «>=»:}
Self. CurHeadRow[CurCol].ElmType:=bc_FuncVal;
Self. CurHeadRow[CurCol].VarInitInRow:=False;
{Формуємо назву функції:}
{якщо змінна має назву змінної двоїстої задачі, то дамо назву
функції прямої задачі, якщо назва прямої – назву двоїстої:}
If Pos (sc_DualTaskVarNameStart, Self. CurHeadRow[CurCol].AsVarName)>0 then
Self. CurHeadRow[CurCol].AsVarName:=sc_YFuncName + IntToStr (CurCol+1)
Else Self. CurHeadRow[CurCol].AsVarName:=sc_DualTaskFuncNameStart +
IntToStr (CurCol+1);
{Якщо переходимо від задачі максимізації до двоїстої задачі
мінімізації, то для нерівності треба буде змінити знак «>=» на «<=»,
(якщо для змінної була умова «>=0», і заголовок для неї був додатний),
тому змінюємо знак заголовка:}
IfDualTType=dt_MaxToMinthen
ChangeSignForValOrVarName (Self. CurHeadRow[CurCol]);
End {Якщо змінна вільна:}
Else if Self. CurHeadRow[CurCol].ElmType=bc_IndependentVar then
Begin{Ця комірка буде заголовком умови-рівняння:}
Self. CurHeadRow[CurCol].ElmType:=bc_Number;
Self. CurHeadRow[CurCol].AsNumber:=0;
End {Якщо це число:}
Else if Self. CurHeadRow[CurCol].ElmType=bc_Number then
Begin
If Self. CurHeadRow[CurCol].AsNumber=1 then {якщо це множник вільних членів}
Begin
Self. CurHeadRow[CurCol].ElmType:=NewDFuncType;
Self. CurHeadRow[CurCol].VarInitInRow:=False;
{Формуємо назву функції мети двоїстої задачі
(залежно від назви функції мети поданої задачі):}
If Pos (sc_DualDestFuncHdr,
Self. CurHeadCol [Length(Self. CurHeadCol) – 1].AsVarName)>0 then
Self. CurHeadRow[CurCol].AsVarName:=sc_DestFuncHdr
Else Self. CurHeadRow[CurCol].AsVarName:=sc_DualDestFuncHdr;
End;
End;
End;
{Для стовпця-заголовка, що стане рядком-заголовком:}
For CurRow:=0 to Length (Self. CurHeadCol) – 1 do
Begin
{Проходимо по усіх елементах-заголовках рядків, і останньому елементу –
заголовку рядка функції мети:}
IfSelf. CurHeadCol[CurRow].ElmType=bc_FuncValthen{Якщо нерівність «<=»:}
Begin
Self. CurHeadCol[CurRow].ElmType:=bc_DependentVar; {буде змінна >=0}
Self. CurHeadCol[CurRow].VarInitInRow:=True;
{Формуємо назву змінної:
якщо функція-нерівність має назву функції двоїстої задачі, то
дамо назву змінної прямої задачі, якщо назва прямої – назву двоїстої:}
If Pos (sc_DualTaskFuncNameStart, CurHeadCol[CurRow].AsVarName)>0 then
Self. CurHeadCol[CurRow].AsVarName:=sc_XVarName + IntToStr (CurRow+1)
Else Self. CurHeadCol[CurRow].AsVarName:=sc_DualTaskVarNameStart +
IntToStr (CurRow+1);
{Якщо переходимо від задачі мінімізації до двоїстої задачі
максимізації, то для змінної треба буде змінити знак і умову «<=0»
на «>=0», (якщо для нерівність була зі знаком «<=», і заголовок для
неї був додатний), тому змінюємо знак заголовка:}
If DualTType=dt_MinToMax then
ChangeSignForValOrVarName (Self. CurHeadCol[CurRow]);
End
Else if Self. CurHeadCol[CurRow].ElmType=bc_Number then
Begin
If Self. CurHeadCol[CurRow].AsNumber=0 then {Якщо 0, заголовок рівняння:}
Begin
Self. CurHeadCol[CurRow].ElmType:=bc_IndependentVar;
Self. CurHeadCol[CurRow].VarInitInRow:=True;
{Формуємо назву змінної двоїстої задачі
(залежно від назви функції мети поданої задачі):}
If Pos (sc_DualDestFuncHdr,
Self. CurHeadCol [Length(Self. CurHeadCol) – 1].AsVarName)>0 then
Self. CurHeadCol[CurRow].AsVarName:=sc_XVarName+IntToStr (CurRow+1)
Else Self. CurHeadCol[CurRow].AsVarName:=sc_DualTaskVarNameStart+
IntToStr (CurRow+1);
End;
End {Якщо заголовок рядка функції мети:}
Else if Self. CurHeadCol[CurRow].ElmType=OldDFuncType then
Begin
Self. CurHeadCol[CurRow].ElmType:=bc_Number;
Self. CurHeadCol[CurRow].AsNumber:=1; {буде множник стовпця вільних членів}
End;
End;
{Міняємо рядок і стовпець-заголовки таблиці місцями:}
SafeMas:=Self. CurHeadRow;
Self. CurHeadRow:=Self. CurHeadCol;
Self. CurHeadCol:=SafeMas;
{У новому стовпці-заголовку шукаємо комірки-заголовки нерівностей «>=».
Їх заміняємо на «<=» множенням рядка на -1:}
For CurRow:=0 to Length (Self. CurHeadCol) – 1 do
Begin
If Self. CurHeadCol[CurRow].ElmType=bc_FuncVal then
Begin
If ValSign (Self. CurHeadCol[CurRow])=bc_Negative then
Self. ChangeSignsInRow(CurRow);
End;
End;
{У новому рядку-заголовку шукаємо комірки-заголовки залежних змінних,
які мають умову «<=0». Змінюємо цю умову на «>=0» множенням стовпця на -1:}
For CurCol:=0 to Length (Self. CurHeadRow) – 1 do
Begin
If Self. CurHeadRow[CurCol].ElmType=bc_DependentVar then
Begin
If ValSign (Self. CurHeadRow[CurCol])=bc_Negative then
Self. ChangeSignsInCol(CurCol);
End;
End;
{Відображаємо отриману таблицю у екранній таблиці:}
Self. WriteTableToGrid (Self.CHeadColNum, Self.CHeadRowNum);
MakeDualLTask:=True;
End;
Function TGridFormattingProcs. PrepareToSolveEqsWithM1: Boolean;
Const sc_CurProcName='PrepareToSolveEqsWithM1';
Var CurRow, ColToDel: Integer;
Begin
If (Self. CurFormatState=fs_EnteringEqs) or
(Self. CurFormatState=fs_NoFormatting) then
Begin
{Якщо таблиця не зчитана, то читаємо:}
If (Self. CurGridModified) and (Self. CurFormatState=fs_EnteringEqs) then
Begin
If Not (Self. GetTask) then
Begin
PrepareToSolveEqsWithM1:=False; Exit;
End;
End;
If Self. TaskHeight<=0 then {Якщо таблиця пуста:}
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_EmptyTable);
PrepareToSolveEqsWithM1:=False;
Exit;
End;
If Not (Self. EqM1TaskPrepared) then
Begin
{Копіюємо стовпець вільних членів (правих частин рівнянь) із
останнього стовпця таблиці до стовпця-заголовка:}
For CurRow:=0 to Length (Self. CurHeadCol) – 1 do
Begin
Self. CurHeadCol[CurRow].ElmType:=bc_Number;
Self. CurHeadCol[CurRow].AsNumber:=
Self. CurTable [CurRow, Length (CurTable[CurRow]) – 1];
End;
{Видаляємо цей останній стовпець із таблиці:}
ColToDel:=Length (Self. CurTable[0]) – 1;
DelColsFromMatr (Self. CurTable, ColToDel, 1);
DeleteFromArr (Self. CurHeadRow, ColToDel, 1);
End;
{Позиціювання відображення таблиці у даному режимі вирішування:}
Self.CHeadColNum:=CurGrid. FixedCols;
Self.CHeadRowNum:=CurGrid. FixedRows-1;
{Відображаємо таблицю, що підготована для розв'язування:}
Self. WriteTableToGrid (Self.CHeadColNum, Self.CHeadRowNum);
{Якщо таблиця пуста після перенесення останнього стовпця у
стовпець-заголовок:}
If Self. TaskHeight<=0 then
Begin
PrepareToSolveEqsWithM1:=False;
Exit;
End;
Self. EqM1TaskPrepared:=True;
PrepareToSolveEqsWithM1:=True;
End
Else
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_WrongEditMode);
PrepareToSolveEqsWithM1:=False;
End;
End;
Function TGridFormattingProcs. PrepareToSolveEqsWithM2: Boolean;
Const sc_CurProcName='PrepareToSolveEqsWithM2';
Var CurRow: Integer;
Begin
If (Self. CurFormatState=fs_EnteringEqs) or
(Self. CurFormatState=fs_NoFormatting) then
Begin {Якщо таблиця не зчитана, то читаємо:}
If (Self. CurGridModified) and (Self. CurFormatState=fs_EnteringEqs) then
Begin
If Not (Self. GetTask) then
Begin
PrepareToSolveEqsWithM2:=False; Exit;
End;
End;
If Self. TaskHeight<=0 then {Якщо таблиця пуста:}
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_TableIsNotReady);
PrepareToSolveEqsWithM2:=False; Exit;
End;
If Not (Self. EqM2TaskPrepared) then
Begin
For CurRow:=0 to Length (Self. CurHeadCol) – 1 do
Begin
{Заповнюємо стовпець-заголовок нулями:}
Self. CurHeadCol[CurRow].ElmType:=bc_Number;
Self. CurHeadCol[CurRow].AsNumber:=0;
{Змінюємо знаки у останньому стовпці таблиці – стовпці вільних
членів. Так як вони у правих частинах рівнянь, то знаходячись у
таблиці коефіцієнтів лівих частин, повинні бути з протилежними
знаками:}
Self. CurTable [CurRow, Length (CurTable[CurRow]) – 1]:=
– Self. CurTable [CurRow, Length (CurTable[CurRow]) – 1];
End;
End;
{Позиціювання відображення таблиці у даному режимі вирішування:}
Self.CHeadColNum:=CurGrid. FixedCols;
Self.CHeadRowNum:=CurGrid. FixedRows-1;
{Відображаємо таюдицю, що підготована для розв'язування:}
Self. WriteTableToGrid (Self.CHeadColNum, Self.CHeadRowNum);
Self. EqM2TaskPrepared:=True;
PrepareToSolveEqsWithM2:=True;
End
Else
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_WrongEditMode);
PrepareToSolveEqsWithM2:=False;
End;
End;
{TTableFormatState=(fs_EnteringEqs, fs_EnteringLTask, fs_SolvingEqsM1,
fs_SolvingEqsM2, fs_SolvingLTask,
fs_NoFormatting, fs_FreeEdit);}
Function TGridFormattingProcs. PrepareToSolveLTask: Boolean;
Const sc_CurProcName='PrepareToSolveLTask';
Begin
If (Self. CurFormatState=fs_EnteringLTask) or
(Self. CurFormatState=fs_NoFormatting) then
Begin {Якщо таблиця у режимі редагування задачі, і модифікована, то зчитуємо:}
If (Self. CurGridModified) and (Self. CurFormatState=fs_EnteringLTask) then
Begin
If Not (Self. GetTask) then {зчитуємо таблицю (умову) з екранної таблиці}
Begin
PrepareToSolveLTask:=False; Exit;
End;
End;
If Self. TaskHeight<=0 then {Якщо таблиця пуста:}
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_TableIsNotReady);
PrepareToSolveLTask:=False; Exit;
End;
If Not (Self.LTaskPrepared) then {якщо ця підготовка ще не виконувалася:}
Begin
{Зсуваємо рядки цільових функцій вниз.
При цьому позначки порядку
рядків залишаємо на тих самих місцях (і присвоюємо тим рядкам, які
стають на ці місця):}
Self. ShiftRowsDown([bc_DestFuncToMax, bc_DestFuncToMin], True);
{Позиціювання відображення таблиці у даному режимі вирішування:}
Self.CHeadColNum:=CurGrid. FixedCols;
Self.CHeadRowNum:=CurGrid. FixedRows-1;
{Відображаємо таблицю, що підготована для розв'язування:}
Self. WriteTableToGrid (Self.CHeadColNum, Self.CHeadRowNum);
Self.LTaskPrepared:=True;
End;
PrepareToSolveLTask:=True;
End
Else
Begin
If Self. CurOutConsole<>Nil then
Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_WrongEditMode);
PrepareToSolveLTask:=False;
End;
End;
Function TGridFormattingProcs. PrepareDFuncForSimplexMaximize: Boolean;
Var ToMax: Boolean; Row, Col, CurWidth, DFuncRowNum: Integer;
Const sc_CurProcName='PrepareDFuncForSimplexMaximize';
Begin
CurWidth:=Length (Self. CurHeadRow);
DFuncRowNum:=Length (Self. CurHeadCol) – 1;
Case Self. CurHeadCol[DFuncRowNum].ElmType of {перевіряємо тип функції мети:}
bc_DestFuncToMax: ToMax:=True;
bc_DestFuncToMin: ToMax:=False;
Else{якщо заданий рядок виявився не функцією мети:}
Begin
If Self. CurOutConsole<>Nil then
|