Курсовая работа: Багатокритеріальна задача лінійного програмування
Название: Багатокритеріальна задача лінійного програмування Раздел: Рефераты по информатике, программированию Тип: курсовая работа | |||||||||||||||||||||||||||||||||||||||||||||
Розв’язати багатокритеріальну задачу лінійного програмування з отриманням компромісного розв’язку за допомогою теоретико-ігрового підходу. Задача (варіант 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 У цій роботі реалізовано вирішування таких задач лінійного програмування: розв’язування задач багатокритеріальної оптимізації, тобто пошук компромісного рішення для задач з кількома функціями мети. Ця задача така: Задано об’єкт управління, що має 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. В результаті отримуємо матрицю з невід’ємними елементами. На головній діагоналі усі вони рівні максимальному значенню. Така заміна матриці не змінює рішення гри, змінює тільки її ціна. Тобто тепер гра має вигляд не гри програшів, а гри з пошуком максимального виграшу. Для пошуку оптимальної стратегії для першого гравця гра подається як пара взаємнодвоїстих однокритеріальних задач ЛП. Для першого гравця потрібні значення змінних двоїстої задачі :
Розв’язавши цю задачу і отримавши оптимальні значення max(Z) = min(W) , що досягаються при значеннях змінних двоїстої задачі , можна обчислити вагові коефіцієнти для компромісного розв’язку багатокритеріальної задачі: , Компромісний вектор значень змінних для багатокритеріальної задачі є лінійною комбінацією оптимальних векторів кожної функції мети. Це сума векторів, що помножені кожен на свій ваговий коефіцієнт: Підставивши цей компромісний вектор в кожну функцію мети багатокритеріальної задачі отримуємо компромісні значення цих функцій. Рівняння, нерівності та функції записуються у таблицю: Розв’язування задачі ЛП для кожної функції мети окремо: Пошук оптимального розв’язку для функції 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 Вирішування закінчено. Успішно. Модуль опису класу, що виконує роботу з задачами ЛП: 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 Self. CurOutConsole. Lines. Add (sc_CurProcName+ sc_CurRowNotMarkedAsDestFunc+IntToStr (DFuncRowNum+1)); PrepareDFuncForSimplexMaximize:=False; Exit; End; End; {Готуємо умову для вирішування симплекс-методом максимізації:} {Міняємо знаки у елементів рядка-заголовка, окрім знака останньої комірки – то множник для стовпця правих частин. Це є інтерпретацією перенесення усіх доданків у праву частину, і форматом для виконання модифікованих Жорданових виключень:} For Col:=0 to CurWidth-2 do ChangeSignForValOrVarName (Self. CurHeadRow[Col]); {Якщо треба шукати максимум, то множимо коефіцієнти функції мети на -1 (окрім вільгого члена), бо помножили і усі x 1… xn на -1. Якщо треба мінімум, то ці коефіцієнти не множимо (бо x 1… xn вже помножені), але множимо вільний член функції. Тоді отримаємо протилежну функцію, щоб знайти її максимум (це протилежний мінімум заданої функції):} Row:=Length (Self. CurHeadCol) – 1; {рядок функції мети} If ToMax then Begin For Col:=0 to CurWidth-2 do {коефіцієнти функції мети міняють знаки:} Self. CurTable [Row, Col]:=-Self. CurTable [Row, Col]; End Else {Якщо треба знайти мінімум:} Begin{Множимо вільний член функції мети на -1:} Self. CurTable [Row, CurWidth-1]:=-Self. CurTable [Row, CurWidth-1]; {Назва функції теж міняє знак:} ChangeSignForValOrVarName (Self. CurHeadCol[Row]); {Тепер це протилежна функція для максимізації:} Self. CurHeadCol[Row].ElmType:=bc_DestFuncToMax; End; PrepareDFuncForSimplexMaximize:=True; End; Function TGridFormattingProcs. PrepareDestFuncInMultiDFuncLTask ( SFuncRowNum, MinDestFuncRowNum: Integer):Boolean; {Готує таблицю для розв'язування задачі ЛП відносно одної заданої функції мети із багатокритеріальної задачі. Вхідні дані: SFuncRowNum – номер рядка у таблиці Self . CopyTable (і комірки у стовпці-заголовку Self . CopyHeadCol ), в якому записана портібна функція мети; DestFuncMinRowNum – номер найвищого (з найменшим номером) рядка функції мети. Усі функції мети мають бути зібрані внизу таблиці; Self . CopyTable – таблиця коефіцієнтів та вільних членів; Self . CopyHeadRow – рядок-заголовок зі змінними та одиницею-множником стовпця вільних членів (має бути останнім); Self . CopyHeadCol – стовпець-заголовок з іменами функцій-нерівностей, нулями (заголовки рядків-рівнянь), іменами функцій мети (що максимізуються (тип комірки bc _ DestFuncToMax ) або мінімізуються (тип bc _ DestFuncToMin )). Вихідні дані: Умова для одної функції: Self . CurTable – таблиця коефіцієнтів та вільних членів з одною функцією мети в останньому рядку, для максимізації симплекс-методом; Self . CurHeadRow – рядок-заголовок; Self . CurHeadCol – стовпець-заголовок з іменами функцій-нерівностей, нулями (заголовки рядків-рівнянь), і одною коміркою функції мети (остання, найнижча комірка), яку треба максимізувати. Якщо у цій комірці перед назвою функції стоїть знак «–», то після максимізації її треба замінити на протилежну функцію (і отримати мінімізацію тої функції, яка була задана в умові). Підпрограма повертає ознаку успішності підготовки умови із одною заданою функцією мети.} Var Row, Col, CurWidth, CurHeight: Integer; Const sc_CurProcName='PrepareDestFuncInMultiDFuncLTask'; Label LStopLabel; Begin If Not (Self. GoToEnd) then Begin{Демонструємо функцію мети у таблиці, з якою будемо працювати:} {Таблиця багатокритеріальної задачі для відображення:} Self. CurHeadRow:=Self. CopyHeadRow; Self. CurHeadCol:=Self. CopyHeadCol; Self. CurTable:=Self. CopyTable; {Координати рядка функції для помітки його кольором:} Self. CurGridSolveCol:=Self.CHeadColNum; Self. CurGridSolveRow:=SFuncRowNum+Self.CHeadRowNum+bc_LTaskRowsBeforeVars; {Відображаємо і чекаємо реакції користувача:} WaitForNewStep (Self.CHeadColNum, Self.CHeadRowNum); If Self. Stop then Goto LStopLabel; End; CurWidth:=Length (Self. CopyHeadRow); CurHeight:=Length (Self. CopyHeadCol); If (SFuncRowNum<0) or (MinDestFuncRowNum<0) or (SFuncRowNum>=CurHeight) or (MinDestFuncRowNum>=CurHeight) then Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_RowNumsIsOutOfTable); PrepareDestFuncInMultiDFuncLTask:=False; Exit; End; {Формуємо умову однокритеріальної задачі лінійного програмування із копії умови багатокритеріальної задачі:} {Копіюємо заголовки і таблицю коефіцієнтів:} SetLength (Self. CurHeadRow, CurWidth); {довжина для рядка заголовка така сама} For Col:=0 to CurWidth-1 do Self. CurHeadRow[Col]:=Self. CopyHeadRow[Col]; {Стовпець-заголовок і висота таблиці мають усі рядки умов (рівнянь та нерівностей) і один рядок функції мети:} SetLength (Self. CurHeadCol, MinDestFuncRowNum+1); SetLength (Self. CurTable, MinDestFuncRowNum+1, CurWidth); For Row:=0 to MinDestFuncRowNum-1 do {копіюємо рядки умов:} Begin Self. CurHeadCol[Row]:=Self. CopyHeadCol[Row]; For Col:=0 to CurWidth-1 do Self. CurTable [Row, Col]:=Self. CopyTable [Row, Col]; End; {В останній рядок таблиці однокритеріальної задачі копіюємо заданий рядок функції мети із багатокритеріальної задачі:} Row:=MinDestFuncRowNum; {номер останнього рядка у однокритеріальній задачі} Self. CurHeadCol[Row]:=Self. CopyHeadCol[SFuncRowNum]; For Col:=0 to CurWidth-1 do Self. CurTable [Row, Col]:=Self. CopyTable [SFuncRowNum, Col]; PrepareDestFuncInMultiDFuncLTask:=Self. PrepareDFuncForSimplexMaximize; Exit; LStopLabel: PrepareDestFuncInMultiDFuncLTask:=False; Exit; End; Procedure TGridFormattingProcs. ShowLTaskResultCalc (DualTaskVals: Boolean); {Процедура зчитує значення функції мети у таблиці розв'язаної однокритеріальної задачі, і значення усіх змінних або функцій в цьому розв'язку. Відображає значення цих змінних, функцій-нерівностей, і функції мети в Self. CurOutConsole. Вхідні дані: DualTaskVals – вмикач режиму відображення значень двоїстої задачі: читаються значення змінних і функцій двоїстої задачі. Їхні значення розміщені не на місці стовпця вільних членів, а у рядку коефіцієнтів функції мети (функції мети прямої задачі). Вони є значеннями змінних чи функцій, імена яких у рядку-заголовку. Змінні чи функції-нерівності двоїстої задачі з іменами у стовпці-заголовку є рівними нулю. Таблиця розв'язаної однокритеріальної (з одною функцією мети) задачі: Self . CurTable – таблиця коефіцієнтів та вільних членів; Self . CurHeadRow – рядок-заголовок з іменами змінних, іменами функцій-нерівностей (що перейшли в рядок-заголовок) та одиницею-множником стовпця вільних членів (має бути останнім); Self . CurHeadCol – стовпець-заголовок з іменами функцій-нерівностей, іменами змінних (виключених), іменем функції мети.} Const DestFuncsTypes=[bc_DestFuncToMax, bc_DestFuncToMin]; Var st1: String; CurColNum, CurRowNum, LastColNum, LastRowNum: Integer; Begin If Self. CurOutConsole<>Nil then Begin LastColNum:=Length (Self. CurHeadRow) – 1; LastRowNum:=Length (Self. CurHeadCol) – 1; st1:=sc_ResultIs; If DualTaskVals then st1:=st1+sc_ForDualTask Else st1:=st1+sc_ForDirectTask; Self. CurOutConsole. Lines. Add(st1); Self. CurOutConsole. Lines. Add (sc_InHeadRow); {Показуємо значення змінних (або функцій) у рядку-заголовку:} For CurColNum:=0 to LastColNum-1 do Begin st1:=''; If Self. CurHeadRow[CurColNum].ElmType=bc_Number then st1:=st1+FloatToStr (Self. CurHeadRow[CurColNum].AsNumber) Else st1:=st1+Self. CurHeadRow[CurColNum].AsVarName; st1:=st1 + sc_Space+sc_Equal+sc_Space; {Усі змінні прямої задачі (або функції) у рядку-заголовку в точці задачі рівні нулю, а змінні двоїстої – у рядку коефіцієнтів функції мети:} If DualTaskVals then st1:=st1+ FloatToStr (Self. CurTable [LastRowNum, CurColNum]) Else st1:=st1+'0'; st1:=st1+sc_KrKm; Self. CurOutConsole. Lines. Add(st1); End; Self. CurOutConsole. Lines. Add (sc_InHeadCol); For CurRowNum:=0 to LastRowNum do Begin st1:=''; If Self. CurHeadCol[CurRowNum].ElmType=bc_Number then st1:=st1+FloatToStr (Self. CurHeadCol[CurRowNum].AsNumber) Else st1:=st1+Self. CurHeadCol[CurRowNum].AsVarName; st1:=st1 + sc_Space+sc_Equal+sc_Space; {Усі змінні прямої задачі (або функції) у стовпці-заголовку в точці задачі мають свої значення у стовпці вільних членів, а змінні двоїстої – рівні нулю:} If (Self. CurHeadCol[CurRowNum].ElmType in DestFuncsTypes) or Not(DualTaskVals) then st1:=st1+ FloatToStr (Self. CurTable [CurRowNum, LastColNum]) Else st1:=st1+'0'; If (Self. CurHeadCol[CurRowNum].ElmType in DestFuncsTypes) then st1:=sc_ResFunc+sc_Space+st1; If CurRowNum=LastRowNum then st1:=st1+sc_Spot Else st1:=st1+sc_KrKm; Self. CurOutConsole. Lines. Add(st1); End; End; End; Procedure TGridFormattingProcs. ReadCurFuncSolution (Var SDValVecs:TFloatMatrix; Var SDDestFuncVals:TFloatArr; SVecRow: Integer; ToReadFuncVals: Boolean; DualTaskVals: Boolean); {Процедура зчитує значення функції мети у таблиці розв'язаної однокритеріальної задачі, і значення усіх змінних або функцій в цьому розв'язку. Вхідні дані: SVecRow – номер поточної функції мети (нумерація з нуля) у масивах SDValVecs і SDDestFuncVals ; ToReadFuncVals – перемикач: якщо рівний False , то зчитуються значення змінних (і значення функції мети); True – зчитуються значення функцій-нерівностей (і значення функції мети); DualTaskVals – вмикач режиму читання змінних двоїстої задачі: читаються значення змінних і функцій двоїстої задачі. Їхні значення розміщені не на місці стовпця вільних членів, а у рядку коефіцієнтів функції мети (функції мети прямої задачі). Вони є значеннями змінних чи функцій, імена яких у рядку-заголовку. Змінні чи функції-нерівності двоїстої задачі з іменами у стовпці-заголовку є рівними нулю. Таблиця розв'язаної однокритеріальної (з одною функцією мети) задачі: Self . CurTable – таблиця коефіцієнтів та вільних членів; Self . CurHeadRow – рядок-заголовок з іменами змінних, іменами функцій-нерівностей (що перейшли в рядок-заголовок) та одиницею-множником стовпця вільних членів (має бути останнім); Self . CurHeadCol – стовпець-заголовок з іменами функцій-нерівностей, іменами змінних (виключених), іменем функції мети. Функція мети має бути в останньому рядку, і бути одна; SDValVecs – масив для запису векторів значень змінних; SDDestFuncVals – масив для запису значень функцій мети (для цих двох останніх масивів пам'ять має бути вже виділеною). Вихідні дані: SDValVecs – масив векторів значень змінних із заповненим вектором номер SVecRow . Змінні, яких немає в таблиці розв'язку, вважаються такими що можуть мати будь-яке значення, і приймаються рівними нулю; SDDestFuncVals – масив значень функцій мети з поточни значенням у комірці номер SVecRow.} Var CurColNum, CurRowNum, LastColNum, LastRowNum: Integer; WorkCellTypes:THeadLineElmTypes; Begin {Ініціюємо нулями поточний вектор значень. Змінні чи функції, імена яких у рядку-заголовку, рівні нулю для прямої задачі (для двоїстої – у стовпці-заголовку). Змінні і функції, яких немає в таблиці, теж вважаємо рівними нулю:} For CurColNum:=0 to Length (SDValVecs[SVecRow]) – 1 do SDValVecs [SVecRow, CurColNum]:=0; {Читаємо стовпець-заголовок і значення із останнього стовпця таблиці:} LastColNum:=Length (Self. CurHeadRow) – 1; LastRowNum:=Length (Self. CurHeadCol) – 1; {Значення функції мети:} SDDestFuncVals[SVecRow]:=Self. CurTable [LastRowNum, LastColNum]; {Функції-нерівності прямої задачі відповідають змінним двоїстої задачі за позиціюванням в заголовках (не за значеннями, значення різні!), змінні прямої – функціям двоїстої:} If (ToReadFuncVals) xor (DualTaskVals) then WorkCellTypes:=[bc_FuncVal] Else WorkCellTypes:=[bc_IndependentVar, bc_DependentVar]; {Читаємо змінні або функції-нерівності (в залежності від того, що задано прочитати):} If DualTaskVals then Begin For CurColNum:=0 to LastColNum-1 do {усі стовпці крім стовпця вільних членів} Begin{значення записуємо у заданий вектор ( SVecRow ):} If (Self. CurHeadRow[CurColNum].ElmType in WorkCellTypes) then SDValVecs [SVecRow, Self. CurHeadRow[CurColNum].VarInitPos]:= Self. CurTable [LastRowNum, CurColNum]; End End Else Begin For CurRowNum:=0 to LastRowNum-1 do {усі рядки крім рядка функції мети} Begin{значення записуємо у заданий вектор ( SVecRow ):} If (Self. CurHeadCol[CurRowNum].ElmType in WorkCellTypes) then SDValVecs [SVecRow, Self. CurHeadCol[CurRowNum].VarInitPos]:= Self. CurTable [CurRowNum, LastColNum]; End End; End; Procedure TGridFormattingProcs. BuildPaymentTaskOfOptim ( Const SOptimXVecs:TFloatMatrix; Const SOptimFuncVals:TFloatArr; SFirstDFuncRow: Integer); {Будує однокритеріальну задачу максимізації для пошуку вагових коефіцієнтів і компромісного вектора значень змінних для усіх заданих функцій мети. Вхідні дані: SOptimXVecs – масив векторів оптимальних значень змінних для кожної з фунуцій мети; SOptimFuncVals – масив оптимальних значень функцій мети; SFirstDFuncRow – номер першої (найвищої) функції мети у Self. CopyTable і Self. CopyHeadCol; Self. CopyTable – матриця коефіцієнтів умови багатокритеріальної задачі; Вихідні дані: Однокритеріальна задача ЛП для максимізації: Self . CurTable – матриця коефіцієнтів оптимальності, вільних членів і коефіцієнтів функції мети; Self . CurHeadCol – імена змінних двоїстої задачі (як функції-нерівності прямої задачі); Self . CurHeadRow – імена функцій-нерівностей двоїстої задачі (як залежні (тільки більше нуля) змінні прямої задачі).} Var jCol, iRow, FuncCount, FuncRow: Integer; MinQ, CurQ:TWorkFloat; Const sc_CurProcName='BuildPaymentTaskOfOptim'; Function CalcQ (ZjFuncRow: Integer; Const XiVals:TFloatArr; Const ZjXj:TWorkFloat):TWorkFloat; {Підраховує міру неоптимальності. Вхідні дані: ZjFuncRow – номер рядка j-ої функції мети у таблиці Self. CopyTable; Self. CopyTable – таблиця коефіцієнтів умови багатокритеріальної задачі ЛП; XiVals – оптимальні значення змінних для i-ої функції мети (для формування i -го рядка матриці неоптимальності); ZjXj – значення j -ої функції мети за j -го набору оптимальних значень змінних (тобто оптимальне значення цієї функції). Для формування j -го стовпця матриці неоптимальності. Вихідні дані: міра неоптимальності.} VarVarNum: Integer; ZjXi:TWorkFloat; Begin ZjXi:=0; {Шукаємо суму добутків значень змінних і коефіцієнтів при них – значення функції у точці, координатами якої є подані значення змінних:} For VarNum:=0 to Length(XiVals) – 1 do ZjXi:=ZjXi + Self. CopyTable [ZjFuncRow, VarNum]*XiVals[VarNum]; CalcQ:=-Abs((ZjXi/ZjXj) – 1); {qij=-|(ZjXi-ZjXj)/(ZjXj)|} End; {Заповнення імен змінних – імен фукнцій двоїстої задачі у рядку-заголовку:} Procedure FillHRowVarName (SCol: Integer); Begin Self. CurHeadRow[SCol].VarInitPos:=SCol; Self. CurHeadRow[SCol].VarInitInRow:=True; Self. CurHeadRow[SCol].ElmType:=bc_DependentVar; Self. CurHeadRow[SCol].AsVarName:=sc_Minus+sc_DualTaskFuncNameStart+ IntToStr (SCol+1); End; {Заповнення у комірки рядка-заголовка числом:} Procedure FillHRowWithNum (SCol: Integer; Const SNumber:TWorkFloat); Begin Self. CurHeadRow[SCol].VarInitPos:=SCol; Self. CurHeadRow[SCol].VarInitInRow:=True; Self. CurHeadRow[SCol].ElmType:=bc_Number; Self. CurHeadRow[SCol].AsNumber:=SNumber; End; {Заповнення імен функцій – імен змінних двоїстої задачі у стовпці-заголовку:} Procedure FillHColFuncName (SRow: Integer); Begin Self. CurHeadCol[SRow].VarInitPos:=SRow; Self. CurHeadCol[SRow].VarInitInRow:=False; Self. CurHeadCol[SRow].ElmType:=bc_FuncVal; Self. CurHeadCol[SRow].AsVarName:=sc_Minus+sc_DualTaskVarNameStart+ IntToStr (SRow+1); End; {Заповнення імені функції мети:} Procedure FillHColDFuncName (SRow: Integer); Begin Self. CurHeadCol[SRow].VarInitPos:=SRow; Self. CurHeadCol[SRow].VarInitInRow:=False; Self. CurHeadCol[SRow].ElmType:=bc_DestFuncToMax; Self. CurHeadCol[SRow].AsVarName:=sc_DestFuncHdr; End; Label LStopLabel; Begin FuncCount:=Length(SOptimFuncVals); If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_CalculatingNoOptMeasures); {Таблиця мір неоптимальності квадратна: кількість стовпців рівна кількості функцій мети; кількість рядків рівна кількості оптимальних векторів значень змінних для кожної з цих функцій (тобто тій же самій кількості). Додатково виділимо один стовпець для вільних членів і один рядок для коефіцієнтів функції мети задачі-інтерпретації гри двох гравців з нульовою сумою, що буде сформована далі:} SetLength (Self. CurTable, FuncCount + 1, FuncCount + 1); {Відповідну довжину задаємо і заголовкам таблиці:} SetLength (Self. CurHeadCol, FuncCount + 1); SetLength (Self. CurHeadRow, FuncCount + 1); {Підраховуємо міри неоптимальності векторів значень змінних для кожної функції мети, і записуємо їх у таблицю коефіцієнтів – формуємо матрицю неоптимальності:} {Шукаємо мінімальну (найбільшу за модулем) міру неоптимальності. Спочатку за неї беремо міру у верхньому лівому куті матриці:} MinQ:=CalcQ (SFirstDFuncRow, SOptimXVecs[0], SOptimFuncVals[0]); Self. CurTable [0, 0]:=MinQ; {записуємо одразу цю міру в матрицю} For jCol:=0 to FuncCount-1 do Begin FuncRow:=SFirstDFuncRow+jCol; {Комірка [0, 0] вже порахована, її обходимо. Для всіх інших виконуємо:} For iRow:=Ord (jCol=0) to FuncCount-1 do {Ord (0=0)=1; Ord (<не нуль>=0)=0} Begin {Підраховуємо міру неоптимальності:} CurQ:=CalcQ (FuncRow, SOptimXVecs[iRow], SOptimFuncVals[jCol]); If MinQ>CurQ then MinQ:=CurQ; {шукаємо найбільшу за модулем міру} Self. CurTable [iRow, jCol]:=CurQ; {записуємо міру в матрицю неоптимальності} End; End; MinQ:=-MinQ; {найбільше абсолютне значення (модуль) усіх мір в матриці} {Заповнюємо заголовки таблиці (це будуть заголовки задачі ЛП):} For jCol:=0 to FuncCount-1 do FillHRowVarName(jCol); For iRow:=0 to FuncCount-1 do FillHColFuncName(iRow); FillHRowWithNum (FuncCount, 1); FillHColDFuncName(FuncCount); {Коефіцієнти функції мети: усі однакові і рівні одиниці (бо відхилення чи наближення будь-якої з цільових функцій від свого оптимального значення пропорційно (у відсотках) має однакову ціну):} For jCol:=0 to FuncCount-1 do Self. CurTable [FuncCount, jCol]:=1; {Вільні члени: усі рівні одиниці:} For iRow:=0 to FuncCount-1 do Self. CurTable [iRow, FuncCount]:=1; {Комірка значення функції мети:} Self. CurTable [FuncCount, FuncCount]:=0; {Ховаємо розв'язувальну комірку у екранній таблиці:} Self. CurGridSolveCol:=0; Self. CurGridSolveRow:=0; WaitForNewStep (Self.CHeadColNum, Self.CHeadRowNum); {показуємо матрицю} If Self. Stop then Goto LStopLabel; {Якщо MinQ=0, то усі міри рівні нулю (бо MinQ тут насправді є максимальним абсолютним значенням). Якщо кількість функцій мети багатокритеріальної задачі рівна одній (тобто задача однокритеріальна), то і міра є лише одна, і для неї MinQ =- q [0,0], тому при додаванні q [0,0]+ MinQ = q [0,0] – q [0,0]=0. Щоб в обох цих випадках розв'язування симплекс-методом працювало коректно, замінимо MinQ на інше число:} If MinQ=0 then Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_AllMeasurIsZero); MinQ:=1 {одиниця, якщо всі нулі (отримаємо матрицю із одиниць)} End Else if Length(SOptimFuncVals)=1 then {якщо всього одна функція мети:} Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_UniqueMeasureCantSetZero); MinQ:=MinQ+1; {збільшимо на 1 – отримаємо матрицю з одною одиницею.} End; {Додаємо до усіх мір неоптимальності максимальну за модулем, і отримуємо матрицю коефіцієнтів, до якої можна застосувати симплекс-метод:} For iRow:=0 to FuncCount-1 do For jCol:=0 to FuncCount-1 do Self. CurTable [iRow, jCol]:=Self. CurTable [iRow, jCol]+MinQ; LStopLabel: End; Procedure TGridFormattingProcs. CalcComprVec (Const SVarVecs:TFloatMatrix; Const SWeightCoefs:TFloatArr; Var DComprVec:TFloatArr); {Обчислює компромісний вектор (масив) значень змінних із із заданих векторів значень і вагових коефіцієнтів для кожного із цих векторів. Вхідні дані: SVarVecs – вектори значень змінних; SWeightCoefs – вагові коефіцієнти для кожного вектора. Вихідні дані: DComprVec – компромісний вектор значень змінних.} Var VecNum, VarNum: Integer; CurComprVal:TWorkFloat; Begin DComprVec:=Nil; If Length(SVarVecs)<=0 then Exit; SetLength (DComprVec, Length (SVarVecs[0])); For VarNum:=0 to Length(DComprVec) – 1 do {для кожної змінної:} Begin CurComprVal:=0; {Множимо значення змінної з кожного вектора на свій ваговий коефіцієнт, і знаходимо суму:} For VecNum:=0 to Length(SVarVecs) – 1 do CurComprVal:=CurComprVal + SVarVecs [VecNum, VarNum]*SWeightCoefs[VecNum]; DComprVec[VarNum]:=CurComprVal; End; End; Function TGridFormattingProcs. CalcDFuncVal (Const SVarVec:TFloatArr; SDestFuncRowNum: Integer):TWorkFloat; {Обчислює значення функції мети за заданих значень змінних. Вхідні дані: SVarVec – вектор значень змінних (в такому порядку, в якому змінні йдуть в рядку-заголовку умови багатокритеріальної задачі); SDestFuncRowNum – номер рядка функції мети в умові задачі у Self . CopyTable ; Self . CopyTable – матриця коефіцієнтів умови багатокритеріальної лінійної задачі оптимізації. Вихідні дані: Повертає значення функції мети.} VarVarNum: Integer; FuncVal:TWorkFloat; Begin FuncVal:=0; For VarNum:=0 to Length(SVarVec) – 1 do {для кожної змінної:} Begin FuncVal:=FuncVal + SVarVec[VarNum]*Self. CopyTable [SDestFuncRowNum, VarNum]; End; CalcDFuncVal:=FuncVal; End; Function TGridFormattingProcs. SolveMultiCritLTask: Boolean; {Вирішування задачі багатокритеріальної оптимізації лінійної форми з використанням теоретико-ігрового підходу. Умовою задачі є умови-нерівності, рівняння та умови на невід'ємність окремих змінних, і декілька функцій мети, для яких треба знайти якомога більші чи менші значення. Вхідні дані: Self . CurTable – таблиця коефіцієнтів та вільних членів; Self . CurHeadRow – рядок-заголовок зі змінними та одиницею-множником стовпця вільних членів (має бути останнім); Self . CurHeadCol – стовпець-заголовок з іменами функцій-нерівностей, нулями (заголовки рядків-рівнянь), іменами функцій мети (що максимізуються (тип комірки bc _ DestFuncToMax ) або мінімізуються (тип bc _ DestFuncToMin )). Функція повертає ознаку успішності вирішування.} Var Row, CurWidth, CurHeight, FirstDestFuncRow, DestFuncCount, VarCount: Integer; Res1: Boolean; st1: String; OptimXVecs, DualUVec:TFloatMatrix; OptimFuncVals, OptGTaskVal, ComprXVec:TFloatArr; Const sc_CurProcName='SolveMultiCritLTask'; sc_TextMarkRow='############'; Procedure ShowWeightCoefs (Const SCoefs:TFloatArr; FirstDestFuncRow: Integer); Var i: Integer; Begin If Self. CurOutConsole<>Nil then Begin Self. CurOutConsole. Lines. Add (sc_WeightCoefs); For i:=0 to Length(SCoefs) – 1 do Begin {Відображаємо вагові коефіцієнти для кожної з функцій мети багатокритеріальної задачі:} Self. CurOutConsole. Lines. Add ('l['+ Self. CopyHeadCol [FirstDestFuncRow+i].AsVarName+'] = '+ FloatToStr (SCoefs[i])); End; End; End; Procedure ShowComprVarVec (Const ComprXVec:TFloatArr); Var Col: Integer; st1: String; Begin If Self. CurOutConsole<>Nil then Begin Self. CurOutConsole. Lines. Add (sc_ComprVarVals); For Col:=0 to Length(ComprXVec) – 1 do Begin st1:=Self. CopyHeadRow[Col].AsVarName + ' = '; st1:=st1 + FloatToStr (ComprXVec[Col]); Self. CurOutConsole. Lines. Add(st1); End; End; End; Procedure ShowDFuncVals (Const ComprXVec:TFloatArr; FirstDFuncRow: Integer); Var Row: Integer; st1: String; Begin If Self. CurOutConsole<>Nil then Begin Self. CurOutConsole. Lines. Add (sc_DestFuncComprVals); For Row:=FirstDFuncRow to Length (Self. CopyTable) – 1 do Begin st1:=Self. CopyHeadCol[Row].AsVarName + ' = '; st1:=st1 + FloatToStr (Self. CalcDFuncVal (ComprXVec, Row)); Self. CurOutConsole. Lines. Add(st1); End; End; End; Label LStopLabel, LFinish; Begin Res1:=True; {прапорець успішності} Self. GetTaskSizes (CurWidth, CurHeight); If CurWidth<=0 then {Якщо таблиця пуста, то задача пуста:} Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_EmptyTable); Self. WasNoRoots:=True; SolveMultiCritLTask:=False; Exit; End; If Self. CurOutConsole<>Nil then Begin Self. CurOutConsole. Lines. Add(''); Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_StartSolving); End; {Зберігаємо посилання на масиви умови багатокритеріальної задачі:} Self. CopyHeadRow:=Self. CurHeadRow; Self. CopyHeadCol:=Self. CurHeadCol; Self. CopyTable:=Self. CurTable; {Шукаємо цільові функції внизу таблиці:} For Row:=CurHeight-1 downto 0 do Begin Case Self. CopyHeadCol[Row].ElmType of bc_DestFuncToMax:; bc_DestFuncToMin:; {Якщо знизу вгору дійшли до рядка, що не є функцією мети – завершуємо:} Else Break; End; End; If Row>=CurHeight-1 then {якщо рядків функцій мети взагалі немає:} Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_NoDestFuncs); Self. WasNoRoots:=True; Res1:=False; Goto LFinish; End Else if Row<0 then {якщо в таблиці є тільки рядки функцій мети:} Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_OnlyDestFuncsPresent); Res1:=False; Goto LFinish; (* Row:=-1; *) End; FirstDestFuncRow:=Row+1; {найвищий у таблиці рядок функції мети} DestFuncCount:=CurHeight-FirstDestFuncRow; {кількість функцій мети} {Змінні: усі стовпці окрім останнього (стовпця вільних членів з одиницею в заголовку):} VarCount:=CurWidth-1; {Вектори змінних в оптимальних розв'язках задач:} SetLength (OptimXVecs, DestFuncCount, VarCount); {Оптимальні значення функцій (максимальні або мінімальні значення):} SetLength (OptimFuncVals, DestFuncCount); {############ Шукаємо min або max кожної функції мети окремо: ############} For Row:=FirstDestFuncRow to CurHeight-1 do {для усіх функцій мети:} Begin If Self. CurOutConsole<>Nil then Begin st1:=sc_TextMarkRow+sc_CurProcName + sc_ForDestFunc+ sc_DoubleQuot+ Self. CopyHeadCol[Row].AsVarName +sc_DoubleQuot+sc_Space; If Self. CopyHeadCol[Row].ElmType=bc_DestFuncToMin then st1:=st1+sc_SearchingMin Else st1:=st1+sc_SearchingMax; st1:=st1+sc_TriSpot+sc_TextMarkRow; Self. CurOutConsole. Lines. Add(st1); End; {Формуємо умову однокритеріальної задачі максимізації:} If Not (Self. PrepareDestFuncInMultiDFuncLTask (Row, FirstDestFuncRow)) then Begin Res1:=False; Break; End; If Self. Stop then Break; {Ховаємо розв'язувальну комірку у екранній таблиці (її нема тут):} Self. CurGridSolveCol:=0; Self. CurGridSolveRow:=0; {Відображаємо підготовану однокритеріальну задачу:} WaitForNewStep (Self.CHeadColNum, Self.CHeadRowNum); If Self. Stop then Break; {Запускаємо вирішування однокритеріальної задачі максимізації лінійної форми (так як поточна функція є функцією максимізації, або зведена до такої):} Self. WasNoRoots:=False; Self. WasManyRoots:=False; Self. SolWasFound:=False; If Not (Self. SolveLTaskToMax(False)) then Begin Res1:=False; Break; End; {Якщо функція мети необмежена або система умов несумісна:} If Not (Self. SolWasFound) then Begin {Якщо функцій мети більше одної, то так як компромісний вектор через необмеженість принаймні одної з функцій мети знайти неможливо:} If (FirstDestFuncRow+1)<CurHeight then Res1:=False Else Res1:=True; Goto LFinish; End; If Self. Stop then Break; {Читаємо вектор значень змінних та оптимальне значення функції мети з таблиці:} Self. ReadCurFuncSolution (OptimXVecs, OptimFuncVals, Row-FirstDestFuncRow, False, False); End; If Not(Res1) then Goto LFinish; If Self. Stop then Goto LStopLabel; {############ Шукаємо міри неоптимальності і будуємо задачу: ############} {######## пошуку компромісних вагових коефіцієнтів, вирішуємо її: ########} If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_TextMarkRow); BuildPaymentTaskOfOptim (OptimXVecs, OptimFuncVals, FirstDestFuncRow); If Self. Stop then Goto LStopLabel; If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_TextMarkRow); {Готуємо задачу до максимізації симплекс-методом:} Res1:=Self. PrepareDFuncForSimplexMaximize; If Not(Res1) then Goto LFinish; {Запускаємо вирішування цієї задачі:} Self. WasNoRoots:=False; Self. WasManyRoots:=False; Self. SolWasFound:=False; {«True» – з відображенням значень двоїстої:} If Not (Self. SolveLTaskToMax(True)) then Begin Res1:=False; Goto LFinish; End; {Якщо функція мети необмежена або система умов несумісна:} If Not (Self. SolWasFound) then Begin Res1:=False; Goto LFinish; End; If Self. Stop then Goto LStopLabel; {############ Обчислюємо вагові коефіцієнти: ############} {Якщо задача-інтерпретація гри вирішена і знайдено оптимальне значення функції, то читаємо це значення і значення змінних двоїстої задачі:} SetLength (OptGTaskVal, 1); {для запису значення функції мети} SetLength (DualUVec, 1, DestFuncCount); {для запису значень змінних} Self. ReadCurFuncSolution (DualUVec, OptGTaskVal, 0, False, True); {Обчислюємо вагові коефіцієнти:} For Row:=0 to DestFuncCount-1 do DualUVec [0, Row]:=(DualUVec [0, Row]/OptGTaskVal[0]); {Li=ui/(W(U))} If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_TextMarkRow); ShowWeightCoefs (DualUVec[0], FirstDestFuncRow); {############ Обчислюємо компромісний вектор: ############} Self. CalcComprVec (OptimXVecs, DualUVec[0], ComprXVec); ShowComprVarVec(ComprXVec); ShowDFuncVals (ComprXVec, FirstDestFuncRow); Goto LFinish; LStopLabel: {Якщо вирішування було перервано:} {Повертаємо початкову умову на попереднє місце:} Self. CurHeadRow:=Self. CopyHeadRow; Self. CurHeadCol:=Self. CopyHeadCol; Self. CurTable:=Self. CopyTable; LFinish: {Обнуляємо посилання на копію умови. Так як це динамічні масиви і щодо них йде відлік кількості посилань, то для них не створюватимуться зайві копії у пам'яті, і при роботі з CurHeadRow , CurHeadCol , CurTable пам'ять буде виділена завжди тільки для їхніх поточних даних:} Self. CopyHeadRow:=Nil; Self. CopyHeadCol:=NIl; Self. CopyTable:=Nil; SolveMultiCritLTask:=Res1; End; Procedure TGridFormattingProcs. ChangeSignsInRow (CurRowNum: Integer); {Зміна знаків у рядку таблиці і відповідній комірці у стовпці-заголовку.} Var CurColNum: Integer; Begin For CurColNum:=0 to Length (Self. CurHeadRow) – 1 do CurTable [CurRowNum, CurColNum]:=-CurTable [CurRowNum, CurColNum]; ChangeSignForValOrVarName (Self. CurHeadCol[CurRowNum]); End; Procedure TGridFormattingProcs. ChangeSignsInCol (CurColNum: Integer); {Зміна знаків у стовпці таблиці і відповідній комірці у рядку-заголовку.} Var CurRowNum: Integer; Begin For CurRowNum:=0 to Length (Self. CurHeadCol) – 1 do CurTable [CurRowNum, CurColNum]:=-CurTable [CurRowNum, CurColNum]; ChangeSignForValOrVarName (Self. CurHeadRow[CurColNum]); End; Function TGridFormattingProcs. ShiftRowsUp (SHeadColElmTypes:THeadLineElmTypes; ToChangeInitPosNums: Boolean=False):Integer; {Функція переміщує рядки таблиці CurTable (разом із відповідними комірками у стовпці-заголовку CurHeadCol ) з заданими типами комірок стовпця-заголовка вгору. Вхідні дані: SHeadColElmTypes – множина типів комірок, що мають бути переміщені вгору (у стовпці-заголовку); ToChangeInitPosNums – вмикач зміни позначок номера по порядку та позначки розташування в таблиці як рядка чи стовпця. Якщо рівний True , то рядки при переміщенні змінюють ці позначки на позначки тих рядків, що були в тих місцях, на які рядки переміщені; Self . CurTable – таблиця коефіцієнтів; Self . CurHeadCol – стовпець-заголовок. Вихідні дані: Self . CurTable і Self . CurHeadCol – таблиця коефіцієнтів і стовпець-заголовок з перенесеними вгору рядками і комірками; функція повертає номер найвищого рядка із тих, що не було задано переміщувати вгору (вище нього – ті, що переміщені вгору).} Var HiNotInSetRow, CurRowToUp, CurRowNum: Integer; Begin {Номер найвищого рядка, що не є в множині тих, які переміщуються вгору. Спочатку ставимо тут номер неіснуючого рядка:} HiNotInSetRow:=-1; {Йдемо по рядкам згори вниз:} For CurRowNum:=0 to Length (Self. CurHeadCol) – 1 do Begin {Шукаємо перший рядок з типом комірки, що не має переміщуватися вгору:} If Not (Self. CurHeadCol[CurRowNum].ElmType in SHeadColElmTypes) then Begin HiNotInSetRow:=CurRowNum; {шукаємо найнижчий рядок, який портібно переміщувати вгору:} For CurRowToUp:=Length (Self. CurHeadCol) – 1 downto CurRowNum+1 do Begin If Self. CurHeadCol[CurRowToUp].ElmType in SHeadColElmTypes then Break; End; {Якщо таких рядків не знайдено, то усі вони вже вгорі:} IfCurRowToUp<=CurRowNumthenBreak Else{Міняємо місцями рядок, що має бути вгорі, і рядок, що не має, але розташований вище:} ChangeRowsPlaces (Self. CurTable, Self. CurHeadCol, CurRowNum, CurRowToUp, ToChangeInitPosNums); End; End; ShiftRowsUp:=HiNotInSetRow; End; Function TGridFormattingProcs. ShiftRowsDown ( SHeadColElmTypes:THeadLineElmTypes; ToChangeInitPosNums: Boolean=False):Integer; {Функція переміщує рядки таблиці CurTable (разом із відповідними комірками у стовпці-заголовку CurHeadCol ) з заданими типами комірок стовпця-заголовка вниз. Вхідні дані: SHeadColElmTypes – множина типів комірок, що мають бути переміщені вниз (у стовпці-заголовку); ToChangeInitPosNums – вмикач зміни позначок номера по порядку та позначки розташування в таблиці як рядка чи стовпця. Якщо рівний True , то рядки при переміщенні змінюють ці позначки на позначки тих рядків, що були в тих місцях, на які рядки переміщені; Self . CurTable – таблиця коефіцієнтів; Self . CurHeadCol – стовпець-заголовок. Вихідні дані: Self . CurTable і Self . CurHeadCol – таблиця коефіцієнтів і стовпець-заголовок з перенесеними донизу рядками і комірками; функція повертає номер найвищого рядка із тих, що переміщені вниз (вище нього – рядки тих типів, що не було задано переміщувати донизу).} VarAllOtherHeadTypes:THeadLineElmTypes; Begin {Отримуємо протилежну множину типів комірок:} AllOtherHeadTypes:=[bc_IndependentVar..bc_OtherType] – SHeadColElmTypes; {Зсуваємо рядки з усіма іншими типами вгору (і рядки з заданими типами залишаються внизу):} ShiftRowsDown:=Self. ShiftRowsUp (AllOtherHeadTypes, ToChangeInitPosNums); End; Function TGridFormattingProcs. SolveLTaskToMax (DualTaskVals: Boolean):Boolean; {Вирішування задачі максимізації лінійної форми (що містить умови- нерівності, рівняння та умови на невід'ємність окремих змінних і одну функцію мети, для якої треба знайти максимальне значення). Вхідні дані: DualTaskVals – вмикач режиму відображення змінних двоїстої задачі (після завершення розв'язування, якщо оптимальне значення знайдено): читаються значення змінних і функцій двоїстої задачі. Їхні значення розміщені не на місці стовпця вільних членів, а у рядку коефіцієнтів функції мети (функції мети прямої задачі). Вони є значеннями змінних чи функцій, імена яких у рядку-заголовку. Змінні чи функції-нерівності двоїстої задачі з іменами у стовпці-заголовку є рівними нулю. Вихідні дані: DResult – тип результату вирішування, який досягнутий (у випадку успішного вирішування); Функція повертає ознаку успішності вирішування.} Const sc_CurProcName='SolveLTaskToMax'; Var CurRowNum, CurRow2N, CurColNum: Integer; HeadRowNum, HeadColNum: Integer; HiNoIndepRow: Integer; ColDeleted, RowDeleted, AllExcluded, WasNothingToDo: Boolean; st1: String; Procedure SearchMNNCellForCol (CurColNum: Integer; StartRowNum, EndRowNum: Integer; Var DRowNum: Integer; AllowNegatCellIfZero: Boolean=False); {Пошук у стовпці CurColNum комірки з МНВ (мінімального невід'ємного відношення вільного члена до значення комірки у стовпці). AllowNegatCellIfZero – дозволити від'ємне значення комірки і при нульовому вільному члені.} Var CurRowNum, FoundRow: Integer; MNN, CurRelat:TWorkFloat; Begin {Шукаємо МНВ у заданому інтервалі рядків:} FoundRow:=-1; MNN:=-1; For CurRowNum:=StartRowNum to EndRowNum do Begin {Перевірка виконання умов невід'ємного відношення:} If (CurTable [CurRowNum, CurColNum]<>0) and (AllowNegatCellIfZero or (CurTable [CurRowNum, Length (Self. CurHeadRow) – 1]<>0) or (CurTable [CurRowNum, CurColNum]>0)) and ((ValSign (CurTable[CurRowNum, Length (Self. CurHeadRow) – 1])* ValSign (CurTable[CurRowNum, CurColNum]))>=0) then Begin CurRelat:=CurTable [CurRowNum, Length (Self. CurHeadRow) – 1]/ CurTable [CurRowNum, CurColNum]; {Якщо знайшли менше, або знайшли перше значення:} If (CurRelat<MNN) or (FoundRow=-1) then Begin MNN:=CurRelat; FoundRow:=CurRowNum; End; End; End; If (Self. CurOutConsole<>Nil) and (FoundRow<0) then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_NoMNN+sc_Space+ IntToStr (CurColNum+1)+sc_Space+sc_TriSpot); DRowNum:=FoundRow; End; Label LStopLabel; Begin If Self. TaskWidth<=0 then {Якщо таблиця пуста, то задача пуста:} Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_EmptyTable); SolveLTaskToMax:=False; Exit; End; HeadRowNum:=Self.CHeadRowNum; HeadColNum:=Self.CHeadColNum; If Self. CurOutConsole<>Nil then Begin Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_StartSolving); Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_ExcludingFreeVars); End; {############## Виключаємо незалежні змінні: ##############} CurRowNum:=0; Repeat WasNothingToDo:=True; AllExcluded:=True; CurColNum:=0; While CurColNum<(Length (Self. CurHeadRow) – 1) do {усі стовпці окрім останнього} Begin ColDeleted:=False; {Координати розв'язувальної комірки для помітки кольором в екранній таблиці:} Self. CurGridSolveCol:=CurColNum+HeadColNum+bc_LTaskColsBeforeVars; Self. CurGridSolveRow:=CurRowNum+HeadRowNum+bc_LTaskRowsBeforeVars; {Якщо поточна змінна незалежна:} If Self. CurHeadRow[CurColNum].ElmType=bc_IndependentVar then Begin{Перевіряємо, чи не дійшли до рядка функції (або взагалі за низ таблиці):} If CurRowNum<(Length (Self. CurHeadCol) – 1) then Begin{якщо рядки для виключення ще залишились:} {Шукаємо ненульову комірку серед коефіцієнтів поточної незалежної змінної (окрім останнього рядка, що є рядком поточної функції мети):} IfSearchNozeroSolveCell (CurRowNum, CurColNum, Length (Self. CurHeadCol) – 2, Length (Self. CurHeadRow) – 2, HeadRowNum, HeadColNum, False) then Begin {якщо змінну можна виключити:} WaitForNewStep (HeadColNum, HeadRowNum); If Self. Stop then Goto LStopLabel; {Обробляємо таблицю модифікованим Жордановим виключенням:} If Not (Self.GI (CurColNum, CurRowNum, Self. CurHeadRow, Self. CurHeadCol, Self. CurTable, ColDeleted, True, True)) then Begin SolveLTaskToMax:=False; Exit; End; WasNothingToDo:=False; {Переходимо до наступного рядка, бо даний рядок тепер вже є рядком виключеної вільної змінної (і змінна виражена як функція-нерівність):} Inc(CurRowNum); End Else{якщо для незалежної змінної усі коефіцієнти обмежень – нулі} Begin{то змінна зовсім незалежна:} {І якщо в рядку функції мети теж нуль, то:} If Self. CurTable [Length(Self. CurHeadCol) – 1, CurColNum]=0 then Begin {хоч змінна й незалежна, від неї теж нічого тут не залежить:} If Self. CurOutConsole<>Nil then Begin st1:=sc_CurProcName+sc_FreeVar; If Self. CurHeadRow[CurColNum].ElmType=bc_Number then st1:=st1+sc_Space+ FloatToStr (Self. CurHeadRow[CurColNum].AsNumber) Else st1:=st1+sc_Space+sc_DoubleQuot+ Self. CurHeadRow[CurColNum].AsVarName+sc_DoubleQuot; Self. CurOutConsole. Lines. Add(st1); End; WaitForNewStep (HeadColNum, HeadRowNum); If Self. Stop then Goto LStopLabel; {Видаляємо стовпець цієї змінної:} DeleteFromArr (Self. CurHeadRow, CurColNum, 1); DelColsFromMatr (Self. CurTable, CurColNum, 1); ColDeleted:=True; WasNothingToDo:=False; End Else AllExcluded:=False; {не усі вільні вдалося виключити} End; End Else AllExcluded:=False; {не усі вільні вдалося виключити} End; If Not(ColDeleted) then Inc(CurColNum); End; {While (CurColNum<(Length (Self. CurHeadRow) – 1)) do…} Until AllExcluded or WasNothingToDo; If Not(AllExcluded) then Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_CantExcludeFreeVars); Self. WriteTableToGrid (HeadColNum, HeadRowNum, True); SolveLTaskToMax:=True; Exit; End; {Переміщаємо рядки з усіма незалежними змінними вгору:} HiNoIndepRow:=Self. ShiftRowsUp([bc_IndependentVar], False); If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_AllFreeVarsExcluded); {Ховаємо розв'язувальну комірку у екранній таблиці:} Self. CurGridSolveCol:=0; Self. CurGridSolveRow:=0; WaitForNewStep (HeadColNum, HeadRowNum); If Self. Stop then Goto LStopLabel; {Якщо усі рядки є рядками незалежних змінних, то номер найвищого рядка іншого типу вважаємо нижче таблиці (бо нема таких рядків):} If HiNoIndepRow<0 then HiNoIndepRow:=Length (Self. CurHeadCol); {Якщо після виключення незалежних змінних не залишилося рядків, окрім рядка функції:} If HiNoIndepRow>=(Length (Self. CurHeadCol) – 1) then Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_NoTableAreaToWork); End; If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_ExcludingZeroRows); {############## Виключаємо 0-рядки. Шукаємо їх: ##############} CurRowNum:=HiNoIndepRow; While CurRowNum<=(Length (Self. CurHeadCol) – 2) do Begin RowDeleted:=False; If Self. CurHeadCol[CurRowNum].ElmType=bc_Number then Begin If Self. CurHeadCol[CurRowNum].AsNumber=0 then {якщо знайшли 0-рядок:} Begin{Для помітки 0-рядка на екранній таблиці:} Self. CurGridSolveCol:=HeadColNum; Self. CurGridSolveRow:=CurRowNum+HeadRowNum+bc_LTaskRowsBeforeVars; WaitForNewStep (HeadColNum, HeadRowNum); If Self. Stop then Goto LStopLabel; {Перевіряємо вільний член рядка, чи він невід'ємний. Якщо від'ємний, то множимо обидві частини рівняння на -1:} If CurTable [CurRowNum, Length (Self. CurHeadRow) – 1]<0 then ChangeSignsInRow(CurRowNum); {Шукаємо у рядку перший додатний коефіцієнт:} For CurColNum:=0 to Length (Self. CurHeadRow) – 2 do If CurTable [CurRowNum, CurColNum]>0 then Break; If CurColNum>(Length (Self. CurHeadRow) – 2) then {Якщо усі недодатні:} Begin If CurTable [CurRowNum, Length (Self. CurHeadRow) – 1]=0 then Begin {Якщо вільний член рівний нулю, то помножимо рівняння на -1:} ChangeSignsInRow(CurRowNum); {Шукаємо у рядку перший додатний коефіцієнт:} For CurColNum:=0 to Length (Self. CurHeadRow) – 2 do If CurTable [CurRowNum, CurColNum]>0 then Break; {Якщо знову додатних нема, значить усі нулі. Видаляємо рядок:} If CurColNum>(Length (Self. CurHeadRow) – 2) then Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_AllZeroInRow+ sc_Space+IntToStr (CurRowNum+1)); DelRowsFromMatr (CurTable, CurRowNum, 1); DeleteFromArr (Self. CurHeadCol, CurRowNum, 1); System. Continue; {переходимо одразу до наступного рядка} End; End Else{Якщо вільний член додатній, а коефіцієнти недодатні, то система несумісна:} Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_DoubleSpot+ sc_Space+sc_NoVals); Self. WasNoRoots:=True; Self. WriteTableToGrid (HeadColNum, HeadRowNum, True); SolveLTaskToMax:=True; Exit; End; End; {Якщо додатний коефіцієнт у 0-рядку обрано, шукаємо МНВ (мінімальне невід'ємне серед відношень вільних членів до членів стовпця, у якому обрали цей коефіцієнт):} SearchMNNCellForCol (CurColNum, HiNoIndepRow, Length (Self. CurHeadCol) – 2, CurRow2N, False); If CurRow2N<0 then {Якщо МНВ не знайдено:} Begin Self. WriteTableToGrid (HeadColNum, HeadRowNum, True); SolveLTaskToMax:=False; Exit; End; {Якщо МНВ знайдено:} Self. CurGridSolveCol:=CurColNum + HeadColNum+bc_LTaskColsBeforeVars; Self. CurGridSolveRow:=CurRow2N + HeadRowNum+bc_LTaskRowsBeforeVars; WaitForNewStep (HeadColNum, HeadRowNum); If Self. Stop then Goto LStopLabel; {Обробляємо таблицю модифікованим Жордановим виключенням:} If Not (Self.GI (CurColNum, CurRow2N, Self. CurHeadRow, Self. CurHeadCol, Self. CurTable, ColDeleted, True, True)) then Begin SolveLTaskToMax:=False; Exit; End; If CurRow2N<>CurRowNum then {Якщо виключили не цей 0-рядок:} System. Continue; {продовжуємо працювати з цим рядком} End; {If Self. CurHeadCol[CurRowNum].AsNumber=0 then…} End; {If Self. CurHeadCol[CurRowNum].ElmType=bc_Number then…} If Not(RowDeleted) then Inc(CurRowNum); End; {While CurRowNum<=(Length (Self. CurHeadCol) – 2) do…} If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_AllZeroRowsExcluded); {Ховаємо розв'язувальну комірку у екранній таблиці:} Self. CurGridSolveCol:=0; Self. CurGridSolveRow:=0; WaitForNewStep (HeadColNum, HeadRowNum); {відмічаємо новий крок} If Self. Stop then Goto LStopLabel; If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_SearchingBaseSolve); {############## Шукаємо опорний розв'язок задачі: ##############} CurRowNum:=HiNoIndepRow; While CurRowNum<=(Length (Self. CurHeadCol) – 2) do Begin {Якщо знайшли від'ємний елемент у стовпці вільних членів:} If Self. CurTable [CurRowNum, Length (Self. CurHeadRow) – 1]<0 then Begin {Для помітки поточного рядка на екранній таблиці:} Self. CurGridSolveCol:=HeadColNum; Self. CurGridSolveRow:=CurRowNum+HeadRowNum+bc_LTaskRowsBeforeVars; WaitForNewStep (HeadColNum, HeadRowNum); If Self. Stop then Goto LStopLabel; {Шукаємо у рядку перший від'ємний коефіцієнт:} For CurColNum:=0 to Length (Self. CurHeadRow) – 2 do If CurTable [CurRowNum, CurColNum]<0 then Break; If CurColNum>(Length (Self. CurHeadRow) – 2) then {Якщо усі невід'ємні:} Begin {Якщо вільний член від'ємний, а коефіцієнти невід'ємні, то система несумісна:} If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_DoubleSpot+sc_Space+ sc_NoVals); Self. WasNoRoots:=True; Self. WriteTableToGrid (HeadColNum, HeadRowNum, True); SolveLTaskToMax:=True; Exit; End; {Якщо від'ємний коефіцієнт у рядку обрано, шукаємо МНВ (мінімальне невід'ємне серед відношень вільних членів до членів стовпця, у якому обрали цей коефіцієнт):} SearchMNNCellForCol (CurColNum, HiNoIndepRow, Length (Self. CurHeadCol) – 2, CurRow2N, False); If CurRow2N<0 then {Якщо МНВ не знайдено:} Begin Self. WriteTableToGrid (HeadColNum, HeadRowNum, True); SolveLTaskToMax:=False; Exit; End; {Якщо МНВ знайдено:} Self. CurGridSolveCol:=CurColNum + HeadColNum+bc_LTaskColsBeforeVars; Self. CurGridSolveRow:=CurRow2N + HeadRowNum+bc_LTaskRowsBeforeVars; WaitForNewStep (HeadColNum, HeadRowNum); If Self. Stop then Goto LStopLabel; {Обробляємо таблицю модифікованим Жордановим виключенням:} If Not (Self.GI (CurColNum, CurRow2N, Self. CurHeadRow, Self. CurHeadCol, Self. CurTable, ColDeleted, True, True)) then Begin SolveLTaskToMax:=False; Exit; End; If CurRow2N<>CurRowNum then {Якщо виключили не цей рядок:} System. Continue; {продовжуємо працювати з цим рядком} End; {If Self. CurTable [CurRowNum, Length (Self. CurHeadRow) – 1]<0 then…} Inc(CurRowNum); End; {While CurRowNum<=(Length (Self. CurHeadCol) – 2) do…} If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_BaseSolveFound); {Ховаємо розв'язувальну комірку у екранній таблиці:} Self. CurGridSolveCol:=0; Self. CurGridSolveRow:=0; WaitForNewStep (HeadColNum, HeadRowNum); {відмічаємо новий крок} If Self. Stop then Goto LStopLabel; If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_SearchingOptimSolve); {############## Шукаємо оптимальний розв'язок задачі: ##############} CurColNum:=0; While CurColNum<=(Length (Self. CurHeadRow) – 2) do Begin ColDeleted:=False; {Якщо знайшли від'ємний коефіцієнт у рядку функції мети:} If CurTable [Length(Self. CurHeadCol) – 1, CurColNum]<0 then Begin {Шукаємо МНВ (мінімальне невід'ємне серед відношень вільних членів до членів стовпця, у якому обрали цей коефіцієнт) серед усіх рядків умов, окрім рядків вільних змінних і рядка функції мети:} SearchMNNCellForCol (CurColNum, HiNoIndepRow, Length (Self. CurHeadCol) – 2, CurRow2N, False); If CurRow2N<0 then {Якщо МНВ не знайдено:} Begin{то функція мети не обмежена зверху, максимальне значення безмежне:} If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_DoubleSpot+sc_Space+ sc_UnlimitedFunc); Self. WasManyRoots:=True; Self. WriteTableToGrid (HeadColNum, HeadRowNum, True); SolveLTaskToMax:=True; Exit; End; {Якщо МНВ знайдено:} Self. CurGridSolveCol:=CurColNum + HeadColNum+bc_LTaskColsBeforeVars; Self. CurGridSolveRow:=CurRow2N + HeadRowNum+bc_LTaskRowsBeforeVars; WaitForNewStep (HeadColNum, HeadRowNum); If Self. Stop then Goto LStopLabel; {Обробляємо таблицю модифікованим Жордановим виключенням:} If Not (Self.GI (CurColNum, CurRow2N, Self. CurHeadRow, Self. CurHeadCol, Self. CurTable, ColDeleted, True, True)) then Begin SolveLTaskToMax:=False; Exit; End; CurColNum:=0; {після виключення могли з'явитися нові від'ємні комірки} System. Continue; End; If Not(ColDeleted) then Inc(CurColNum); End; {Якщо назва функції мети вказана зі знаком «–», то це протилежна функція мети. Змінимо знаки у її рядку, і отримаємо шукану мінімізацію функції:} CurRowNum:=Length (Self. CurHeadCol) – 1; If ValSign (Self. CurHeadCol[CurRowNum])=bc_Negative then Begin ChangeSignsInRow(CurRowNum); Self. CurHeadCol[CurRowNum].ElmType:=bc_DestFuncToMin; End; If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_DoubleSpot+sc_Space+ sc_ValFound); Self. ShowLTaskResultCalc(DualTaskVals); Self. SolWasFound:=True; SolveLTaskToMax:=True; {Ховаємо розв'язувальну комірку у екранній таблиці:} Self. CurGridSolveCol:=0; Self. CurGridSolveRow:=0; WaitForNewStep (HeadColNum, HeadRowNum); Exit; LStopLabel: If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_SolvingStopped); Self. CurGridSolveCol:=0; Self. CurGridSolveRow:=0; SolveLTaskToMax:=False; Exit; End; procedure TGridFormattingProcs. EditLineEqsOnNewRow (Sender: TObject; NewRows: array of Integer); {Підтримує форматування стовпця нумерації таблиці у такому вигляді: 1 2 3 4 5 … m} Var CurNum: Integer; CurGrid:TStringGrid; Begin If Sender=Nil then Exit; {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnNewRow<>Nil then Self. OldOnNewRow (Sender, NewRows); If Sender is TStringGrid then Begin CurGrid:=TStringGrid(Sender); For CurNum:=0 to Length(NewRows) – 1 do Begin {Нумерація з третього рядка, бо два перших – заголовки:} If NewRows[CurNum]>=(Self.CHeadRowNum+1) then Begin CurGrid. Cells [0, NewRows[CurNum]]:=IntToStr (NewRows[CurNum]- Self.CHeadRowNum); End; End; End; End; procedure TGridFormattingProcs. EditLineEqsOnNewCol (Sender: TObject; NewCols: array of Integer); {Підтримує форматування рядка нумерації та рядка-заголовка таблиці у такому вигляді: 1 2 3 4 5… n n +1 x 1 x 2 x 3 x 4 x 5… xn 1 } Var CurNum: Integer; CurGrid:TStringGrid; CurColNumStr: String; Begin If Sender=Nil then Exit; {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnNewCol<>Nil then Self. OldOnNewCol (Sender, NewCols); If Sender is TStringGrid then Begin CurGrid:=TStringGrid(Sender); For CurNum:=0 to Length(NewCols) – 1 do Begin {Заголовки лише для комірок, які можна редагувати:} If NewCols[CurNum]>=(Self.CHeadColNum+1) then Begin CurColNumStr:=IntToStr (NewCols[CurNum] – Self.CHeadColNum); CurGrid. Cells [NewCols[CurNum], 0]:=CurColNumStr; {Останній стовпець – числа у правих частинах рівнянь:} If (NewCols[CurNum]+1)=CurGrid. ColCount then CurGrid. Cells [NewCols[CurNum], 1]:=sc_RightSideValsHdr {в усіх інших – коефіцієнти при змінних X1…Xn:} Else CurGrid. Cells [NewCols[CurNum], 1]:=sc_XVarName+CurColNumStr; End; End; If Length(NewCols)>0 then Begin {Якщо перед оновленими або новими стовпцями були інші стовпці, то в останному з них оновлюємо підпис: тепер він буде з іменем змінної (« xn »), а не з іменем стовпця правих частин рівнянь ( a ). (Тут покладаємося на те, що номери оновлених стовпців сортовані за зростанням):} If NewCols[0]>(Self.CHeadColNum+1) then CurGrid. Cells [NewCols[0] – 1, 1]:=sc_XVarName+IntToStr (NewCols[0]- (Self.CHeadColNum+1)); End Else {Якщо нових стовпців немає (тобто кількість стовпців зменшилася):} Begin {Оновлюємо підпис останнього стовпця (праві частини рівнянь):} CurGrid. Cells [CurGrid. ColCount-1, 1]:=sc_RightSideValsHdr; End; End; End; procedure TGridFormattingProcs. EditLineEqsOnDrawCell (Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); {Процедура виконується при малюванні кожної комірки StringGrid у режимі набору вхідних даних системи лінійних рівнянь. Зафарбовує в інший колір останній стовпець – стовпець правих частин рівнянь.} VarCurGrid:TStringGrid; SafeBrushColor:TColor; Begin If Sender=Nil then Exit; {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnDrawCell<>Nil then Self. OldOnDrawCell (Sender, ACol, ARow, Rect, State); If Sender is TStringGrid then Begin CurGrid:=TStringGrid(Sender); SafeBrushColor:=CurGrid. Canvas. Brush. Color; {Комірки останнього стовпця є стовпцем правих сторін рівнянь. Фарбуємо їх у блакитний колір (окрім комірок заголовка):} If (ACol>=(CurGrid. ColCount-bc_LineEqM2ColsAfterVars)) and (Not (gdFixed in State)) then Begin CurGrid. Canvas. Brush. Color:=lwc_RightSideColColor; {Малюємо текст на фоні з кольором Brush :} CurGrid. Canvas. TextRect (Rect, Rect. Left, Rect. Top, CurGrid. Cells [ACol, ARow]); End; CurGrid. Canvas. Brush. Color:=SafeBrushColor; End; End; procedure TGridFormattingProcs. SolveLineEqsM1OrM2OnDrawCell (Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); {Процедура фарбує комірки (їхній фон) таблиці вирішування системи лінійних рівнянь у стовпці правих частин (вільних членів). У залежності від методу розв'язання цей стопець може бути першим стовпцем-заголовком (1-ий спосіб, з отриманням оберненої матриці коефіцієнтів), або останнім стовпцем (2-ий спосіб, з отриманням нулів у рядку-заголовку і видаленням стовпців цих нулів).} Var CurGrid:TStringGrid; SafeBrushColor:TColor; CurColor:TColor; Begin If Sender=Nil then Exit; {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnDrawCell<>Nil then Self. OldOnDrawCell (Sender, ACol, ARow, Rect, State); If Sender is TStringGrid then Begin CurGrid:=TStringGrid(Sender); SafeBrushColor:=CurGrid. Canvas. Brush. Color; CurColor:=bc_NotColored; If Not (gdFixed in State) then {якщо комірка не у заголовках StringGrid} Begin {У режимі розв'язування способом 1 відмічаємо перший стовпець кольором, а у режимі способу 2 – відмічаємо останній (стовпець правих частин – вільних членів):} If ((Self. CurFormatState=fs_SolvingEqsM1) and (ACol<(Self.CHeadColNum+bc_LineEqM1ColsBeforeVars))) or ((Self. CurFormatState=fs_SolvingEqsM2) and (ACol>=(CurGrid. ColCount-bc_LineEqM2ColsAfterVars))) then CurColor:=lwc_RightSideColColor {Якщо це комірка коефіцієнта при змінній, і задача у ході вирішування:} Else if InSolving then Begin If Self. CurGridSolveCol=ACol then {якщо це розв'язувальний стовпець:} Begin If Self. CurGridSolveRow=ARow then {якщо це розв'язувальна комірка:} CurColor:=lwc_SolveCellColor Else CurColor:=lwc_SolveColColor; End{Якщо це розв'язувальний рядок (але не розв'язувальна комірка):} Else if Self. CurGridSolveRow=ARow then CurColor:=lwc_SolveRowColor; End; End; If CurColor<>bc_NotColored then {якщо комірку треба пофарбувати:} Begin {Малюємо текст на фоні з кольором CurColor:} CurGrid. Canvas. Brush. Color:=CurColor; CurGrid. Canvas. TextRect (Rect, Rect. Left, Rect. Top, CurGrid. Cells [ACol, ARow]); End; CurGrid. Canvas. Brush. Color:=SafeBrushColor; End; End; procedure TGridFormattingProcs. EdLineTaskOnNewRow (Sender: TObject; NewRows: array of Integer); {Процедура працює при виникненні події оновлення рядка чи додавання нового рядка у GrowingStringGrid. Підтримує форматування стовпця нумерації і стовпця-заголовка таблиці у такому вигляді: 1 y 1 2 y 2 3 y 3 4 y 4 5 y 5 … m ym Стовпець-заголовок (нові комірки стовпця-заголовка за змовчуванням заповнюються значеннями типу «функції-нерівності»).} Var CurNum, CurTableRow: Integer; CurGrid:TStringGrid; Begin If Sender=Nil then Exit; {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnNewRow<>Nil then Self. OldOnNewRow (Sender, NewRows); If Sender is TStringGrid then Begin CurGrid:=TStringGrid(Sender); {Освіжаємо масив стовпця-заголовка відповідно до висоти таблиці:} UpdateLTaskHeadColToStrGrid (CurGrid, NewRows); {Відображаємо заголовки оновлених або нових рядків:} For CurNum:=0 to Length(NewRows) – 1 do Begin {Нумерація з першого рядка, що не є рядком заголовків:} If NewRows[CurNum]>=(Self.CHeadRowNum+1) then Begin {Нумерація рядків:} CurGrid. Cells [Self.CHeadColNum-1, NewRows[CurNum]]:= IntToStr (NewRows[CurNum] – Self.CHeadRowNum); {Заголовки із масиву стовпця-заголовка:} CurTableRow:=NewRows[CurNum] – Self.CHeadRowNum-bc_LTaskRowsBeforeVars; CurGrid. Cells [Self.CHeadColNum, NewRows[CurNum]]:= GetValOrNameAsStr (Self. CurHeadCol[CurTableRow]); End; End; {Якщо нові або змінені рядки були, то вважаємо таблицю зміненою:} If Length(NewRows)>0 then Self. CurGridModified:=True; End; End; procedure TGridFormattingProcs. EdLineTaskOnNewCol (Sender: TObject; NewCols: array of Integer); {Підтримує форматування рядка нумерації та рядка-заголовка таблиці у такому вигляді: 1 2 3 4 5… n n +1 y x 1 x 2 x 3 x 4… xn 1 } Var CurNum, CurTableCol: Integer; CurGrid:TStringGrid; Begin If Sender=Nil then Exit; {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnNewCol<>Nil then Self. OldOnNewCol (Sender, NewCols); If Sender is TStringGrid then Begin CurGrid:=TStringGrid(Sender); {Освіжаємо масив поміток залежності змінних x:} Self. UpdateLTaskHeadRowToStrGrid(CurGrid); {Відображаємо заголовки оновлених або нових стовпців:} For CurNum:=0 to Length(NewCols) – 1 do Begin {Заголовки лише для комірок, які можна редагувати:} If NewCols[CurNum]>=Self.CHeadColNum then Begin {Нумерація стовпців:} CurGrid. Cells [NewCols[CurNum], Self.CHeadRowNum-1]:= IntToStr (NewCols[CurNum] – Self.CHeadColNum); {Заголовки із масиву рядка-заголовка:} CurTableCol:=NewCols[CurNum] – Self.CHeadColNum-bc_LTaskColsBeforeVars; CurGrid. Cells [NewCols[CurNum], Self.CHeadRowNum]:= GetValOrNameAsStr (Self. CurHeadRow[CurTableCol]); End; End; If Length(NewCols)>0 then Begin {Якщо нові або змінені стовпці були, то вважаємо таблицю зміненою:} Self. CurGridModified:=True; {Якщо перед оновленими або новими стовпцями були інші стовпці, то в останному з них оновлюємо підпис: тепер він буде з іменем змінної (« xn ») або, якщо це перший стовпець-то з підписом стовпця імен функцій та констант рівнянь. (Тут покладаємося на те, що номери оновлених стовпців сортовані за зростанням):} If NewCols[0]>Self.CHeadColNum+bc_LTaskColsBeforeVars then Begin CurTableCol:=NewCols[0] – 1-Self.CHeadColNum-bc_LTaskColsBeforeVars; CurGrid. Cells [NewCols[0] – 1, Self.CHeadRowNum]:= GetValOrNameAsStr (Self. CurHeadRow[CurTableCol]); End; End Else {Якщо нових стовпців нема (кількість стовпців зменшилася):} {відображаємо останню (найправішу) комірку} CurGrid. Cells [CurGrid. ColCount-1, 1]:= GetValOrNameAsStr (Self. CurHeadRow [CurGrid. ColCount-1- Self.CHeadColNum-bc_LTaskColsBeforeVars]); End; End; procedure TGridFormattingProcs. NumerationOnNewRow (Sender: TObject; NewRows: array of Integer); {Процедура працює при виникненні події оновлення рядка чи додавання нового рядка у GrowingStringGrid. Підтримує форматування стовпця нумерації таблиці у такому вигляді: 1 2 3 4 5 … m} Var CurNum: Integer; CurGrid:TStringGrid; Begin If Sender=Nil then Exit; {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnNewRow<>Nil then Self. OldOnNewRow (Sender, NewRows); If Sender is TStringGrid then Begin CurGrid:=TStringGrid(Sender); For CurNum:=0 to Length(NewRows) – 1 do Begin {Нумерація з першого рядка, що не є рядком заголовків GrowingStringGrid:} If NewRows[CurNum]>=(Self.CHeadRowNum+1) then CurGrid. Cells [0, NewRows[CurNum]]:= IntToStr (NewRows[CurNum] – Self.CHeadRowNum); End; {For CurNum:=0 to Length(NewRows) – 1 do…} End; {If Sender is TStringGrid then…} End; procedure TGridFormattingProcs. NumerationOnNewCol (Sender: TObject; NewCols: array of Integer); {Процедура працює при виникненні події оновлення чи додавання нового стовпця у GrowingStringGrid. Підтримує форматування рядка нумерації таблиці у такому вигляді: 1 2 3 4 5… n} Var CurNum: Integer; CurGrid:TStringGrid; Begin If Sender=Nil then Exit; {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnNewCol<>Nil then Self. OldOnNewCol (Sender, NewCols); If Sender is TStringGrid then Begin CurGrid:=TStringGrid(Sender); For CurNum:=0 to Length(NewCols) – 1 do Begin {Заголовки лише для нефіксованих комірок:} If NewCols[CurNum]>=(Self.CHeadColNum+1) then CurGrid. Cells [NewCols[CurNum], 0]:= IntToStr (NewCols[CurNum] – Self.CHeadColNum); End; End; End; Procedure TGridFormattingProcs. UpdateLTaskHeadRowToStrGrid (SGrid:TStringGrid); {Процедура для підтримки масиву рядка-заголовка під час редагування таблиці. Встановлює довжину масиву відповідно до ширини екранної таблиці і координат вписування в неї таблиці задачі, заповнює нові комірки значеннями за змовчуванням, а також змінює останню комірку перед новими.} Var CurLTaskVarCount, OldCount, CurVarMark: Integer; Begin {Кількість стовпців для коефіцієнтів змінних у таблиці:} CurLTaskVarCount:=SGrid. ColCount-Self.CHeadColNum- bc_LTaskColsBeforeVars {-bc_LTaskColsAfterVars} ; {Якщо таблиця має надто малу ширину, то нічого тут не робимо:} If CurLTaskVarCount<0 then Exit; {Масив видовжуємо до кількості стовпців у StringGrid, у яких редагуємо коєфіцієнти при змінних:} OldCount:=Length (Self. CurHeadRow); If OldCount<>CurLTaskVarCount then Begin SetLength (Self. CurHeadRow, CurLTaskVarCount); {змінюємо довжину} {Заповнюємо нові елементи масиву значеннями за змовчуванням: вільні змінні:} For CurVarMark:=OldCount to CurLTaskVarCount-2 do Begin Self. CurHeadRow[CurVarMark].ElmType:=bc_IndependentVar; Self. CurHeadRow[CurVarMark].VarInitInRow:=True; Self. CurHeadRow[CurVarMark].VarInitPos:=CurVarMark; Self. CurHeadRow[CurVarMark].AsVarName:=sc_XVarName+IntToStr (CurVarMark+1); End; {Останній елемент є числом, а не змінною: це множник стовпця вільних членів (правих частин):} IfCurLTaskVarCount>0 then Begin Self. CurHeadRow [CurLTaskVarCount-1].ElmType:=bc_Number; Self. CurHeadRow [CurLTaskVarCount-1].AsNumber:=1; {Колишній останній елемент тепер буде змінною:} If (OldCount>0) and (OldCount<CurLTaskVarCount) then Begin Self. CurHeadRow [OldCount-1].ElmType:=bc_IndependentVar; Self. CurHeadRow [OldCount-1].AsVarName:=sc_XVarName+IntToStr(OldCount) End; End; End; End; Procedure TGridFormattingProcs. UpdateLTaskHeadColToStrGrid (SGrid:TStringGrid; NewRows: array of Integer); {Процедура для підтримки масиву стовпця-заголовка під час редагування таблиці. Встановлює довжину масиву відповідно до висоти екранної таблиці і координат вписування в неї таблиці задачі, заповнює нові комірки значеннями за змовчуванням. Вхідні дані: SGrid – екранна таблиця, під яку треба настроїти масив; NewRows – масив номерів рядків таблиці, що були додані чи змінені (що зазнали змін з часу останнього виклику цієї процедури під час редагування).} Var CurHeight, OldHeight, CurRow: Integer; Procedure FillWithDefVal (SElmNum: Integer); Begin Self. CurHeadCol[SElmNum].ElmType:=bc_FuncVal; Self. CurHeadCol[SElmNum].VarInitInRow:=False; Self. CurHeadCol[SElmNum].VarInitPos:=SElmNum; Self. CurHeadCol[SElmNum].AsVarName:=sc_YFuncName+ IntToStr (SElmNum+1); End; Begin {Висота таблиці за поточною висотою екранної таблиці:} CurHeight:=SGrid. RowCount-Self.CHeadRowNum-bc_LTaskRowsBeforeVars; OldHeight:=Length (Self. CurHeadCol); {попередня висота таблиці} If (OldHeight<>CurHeight) and (CurHeight>=0) then Begin {Змінюємо довжину масиву стовпця-заголовка:} SetLength (Self. CurHeadCol, CurHeight); For CurRow:=OldHeight to CurHeight-1 do FillWithDefVal(CurRow); {заповнюємо нові комірки за змовчуванням} End; End; procedure TGridFormattingProcs. EdLineTaskOnDrawCell (Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); {Процедура виконується при малюванні кожної комірки StringGrid. Зафарбовує в інший колір фону комірок: – перший стовпець комірок (стовпець-заголовок таблиці задачі лінійного програмування). Комірки цього стовпця зафарбовуються відповідно до типів елементів у масиві стовпця-заголовка (якщо цей масив створений для цих комірок, інакше – за змовчуванням: кольором назв функцій умов-нерівностей, і найнижчу комірку – кольором для назви функції мети); – останній стовпець (стовпець значень правих сторін рівнянь або нерівностей та комірка значення цільової функції); – найнижчий рядок (рядок коефіцієнтів цільової функції); – відмічає кольором комірки-заголовки стовпців коефіцієнтів змінних за відмітками про залежність змінних (рядок-заголовок таблиці задачі ЛП).} Var CurGrid:TStringGrid; SafeBrushColor:TColor; CurVarColState:THeadLineElmType; CurColor:TColor; ArrRowNum: Integer; Begin If Sender=Nil then Exit; {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnDrawCell<>Nil then Self. OldOnDrawCell (Sender, ACol, ARow, Rect, State); ArrRowNum:=ARow – (Self.CHeadRowNum+bc_LTaskRowsBeforeVars); If Sender is TStringGrid then Begin CurGrid:=TStringGrid(Sender); SafeBrushColor:=CurGrid. Canvas. Brush. Color; CurColor:=bc_NotColored; {Комірки останнього стовпця є стовпцем правих сторін рівнянь. Фарбуємо їх у блакитний колір (окрім комірок заголовків):} If Not (gdFixed in State) then {якщо комірка не у заголовках StringGrid} Begin If ACol>=(CurGrid. ColCount-bc_LTaskColsAfterVars) then {останні стовпці:} Begin {Якщо це комірка значення цільової функції – для неї свій колір:} Case Self. CurHeadCol[ArrRowNum].ElmType of bc_DestFuncToMax: CurColor:=lwc_DestFuncValColor; bc_DestFuncToMin: CurColor:=lwc_DestFuncValColor; Else CurColor:=lwc_RightSideColColor; End; End Else if ACol<(Self.CHeadColNum+bc_LTaskColsBeforeVars) then Begin {Якщо перші стовпці (стовпець-заголовок):} {Якщо для цієї комірки задано елемент у масиві стовпця-заголовка, то фарбуємо її залежно від типу цього елемента:} If Length (Self. CurHeadCol)> (ARow – (Self.CHeadRowNum + bc_LTaskRowsBeforeVars)) then Begin{Тип елемента у комірці:} CurVarColState:=Self. CurHeadCol [ARow – (Self.CHeadRowNum+ bc_LTaskRowsBeforeVars)].ElmType; CurColor:=GetColorByElmType(CurVarColState); {колір за типом} End Else{Якщо масив стовпця-заголовка не визначено для комірки – фарбуємо за змовчуванням – як назву функції умови-нерівності:} CurColor:=lwc_HeadColColor; End{Якщо рядок коефіцієнтів при змінних цільової функції:} Else if (Self. CurHeadCol[ArrRowNum].ElmType=bc_DestFuncToMax) or (Self. CurHeadCol[ArrRowNum].ElmType=bc_DestFuncToMin) then Begin {Якщо рядок функції виділений, то виділяємо кольором:} If InSolving and (Self. CurGridSolveRow=ARow) then CurColor:=lwc_SolveRowColor Else CurColor:=lwc_FuncRowColor; {інакше – колір рядка функції мети} End{Якщо це розв'язувальна комірка, чи рядок або стовпець з такою коміркою, і треба відображати хід вирішування задачі:} Else if InSolving then Begin If Self. CurGridSolveCol=ACol then {якщо це розв'язувальний стовпець:} Begin If Self. CurGridSolveRow=ARow then {якщо це розв'язувальна комірка:} CurColor:=lwc_SolveCellColor Else CurColor:=lwc_SolveColColor; End{Якщо це розв'язувальний рядок (але не розв'язувальна комірка):} Else if Self. CurGridSolveRow=ARow then CurColor:=lwc_SolveRowColor; End; End; {Зафарбовуємо комірки-заголовки стовпців коефіцієнтів при змінних відповідно до масиву поміток про залежність:} If (ARow=Self.CHeadRowNum) and (Not (ACol<(Self.CHeadColNum+bc_LTaskColsBeforeVars))) then Begin CurVarColState:=Self. CurHeadRow [ACol – Self.CHeadColNum- bc_LTaskColsBeforeVars].ElmType; CurColor:=GetColorByElmType(CurVarColState) End; If CurColor<>bc_NotColored then {якщо комірку треба пофарбувати:} Begin {Малюємо текст на фоні з кольором CurColor:} CurGrid. Canvas. Brush. Color:=CurColor; CurGrid. Canvas. TextRect (Rect, Rect. Left, Rect. Top, CurGrid. Cells [ACol, ARow]); End; CurGrid. Canvas. Brush. Color:=SafeBrushColor; End; End; procedure TGridFormattingProcs. EdLineTaskOnDblClick (Sender: TObject); {Процедура реагує на подвійне натискання лівою кнопкою миші на комірки рядка-заголовка таблиці (другий рядок StringGrid ). Редагує масив позначок про обрані стовпці ( SipmlexVarsDependencyRec ) залежних змінних. Залежні змінні – це змінні, для яких є умова невід'ємності. Тобто вони не повинні бути менше нуля.} Var CurGrid:TStringGrid; CurCol, CurRow: Integer; MouseCoordsInGrid:TPoint; Begin If Sender=Nil then Exit; {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnDblClick<>Nil then Self. OldOnDblClick(Sender); If Sender is TStringGrid then Begin CurGrid:=TStringGrid(Sender); {Пробуємо узнати, на яку комірку двічі натиснула миша:} MouseCoordsInGrid:=CurGrid. ScreenToClient (Mouse. CursorPos); CurCol:=-1; CurRow:=-1; CurGrid. MouseToCell (MouseCoordsInGrid.X, MouseCoordsInGrid.Y, CurCol, CurRow); {Якщо натиснуто на комірку-заголовок стовпця коефіцієнтів при змінній, то:} If ((CurCol>=(Self.CHeadColNum+bc_LTaskColsBeforeVars)) and (CurCol<(CurGrid. ColCount-bc_LTaskColsAfterVars))) and (CurRow=Self.CHeadRowNum) then Begin {Змінюємо ознаку залежності відповідної змінної:} If CurHeadRow [CurCol – Self.CHeadColNum- bc_LTaskColsBeforeVars].ElmType=bc_IndependentVar then CurHeadRow [CurCol – Self.CHeadColNum- bc_LTaskColsBeforeVars].ElmType:=bc_DependentVar Else CurHeadRow [CurCol – Self.CHeadColNum- bc_LTaskColsBeforeVars].ElmType:=bc_IndependentVar; {Задаємо перемалювання комірок, щоб відобразилася зміна позначки для змінної:} CurGrid. Invalidate; End; End; End; Procedure TGridFormattingProcs. InitGridPopupMenu (SGrid:TStringGrid); {Процедура перевіряє наявність об'єкта TPopupMenu. Якщо його немає (SGrid. PopupMenu=Nil), то створює новий. Видаляє усі пунтки (елементи, теми) з меню.} Begin If SGrid. PopupMenu=Nil then Begin SGrid. PopupMenu:=TPopupMenu. Create(Application); End; SGrid. PopupMenu. AutoPopup:=False; SGrid. PopupMenu. Items. Clear; End; Procedure TGridFormattingProcs. ProcOnCellTypeSelInMenu (Sender: TObject); {Обробник вибору пункту в меню типів для комірки рядка – чи стовпця-заголовка.} Constsc_CurProcName='ProcOnCellTypeSelInMenu'; ProcedureReportUnsupportedCell; Begin {Відображає координати комірки з повідомленням про те, що вона не підтримується:} If Self. CurOutConsole<>Nil then Begin Self. CurOutConsole. Lines. Add (sc_CurProcName + sc_NoCellOrNotSupported+ ' ['+IntToStr (Self. CurGridSolveCol)+';'+IntToStr (Self. CurGridSolveRow)+ ']… '); End; End; Var CurMenuItem:TMenuItem; TypeForCell:THeadLineElmType; Begin If (Sender=Nil) or (Not (Sender is TMenuItem)) then Begin If Self. MemoForOutput<>Nil then Self. MemoForOutput. Lines. Add (sc_CurProcName + sc_CantDetMenuItem); Exit; End; {Читаємо тип, що обраний для комірки:} CurMenuItem:=TMenuItem(Sender); TypeForCell:=THeadLineElmType (CurMenuItem. Tag); If (Self. CurGridSolveCol<0) and (Self. CurGridSolveRow<0) then Begin {якщо комірка вище чи лівіше заголовків таблиці:} ReportUnsupportedCell; Exit; End; {Перевіряємо координати комірки і змінюємо її тип:} {координати комірки мають бути записані у CurGridSolveRow і CurGridSolveCol:} If Self. CurGridSolveRow=-bc_LTaskRowsBeforeVars then Begin{якщо це комірка рядка-заголовка:} If Length (Self. CurHeadRow)>Self. CurGridSolveCol then {якщо комірка існує:} Begin {задаємо тип комірки:} Self. CurHeadRow [Self. CurGridSolveCol].ElmType:=TypeForCell; End Else{якщо в рядку-заголовку немає такої комірки:} Begin ReportUnsupportedCell; Exit; End; End Else if Self. CurGridSolveCol=-bc_LTaskColsBeforeVars then Begin {якщо це комірка стовпця-заголовка:} If Length (Self. CurHeadCol)>Self. CurGridSolveRow then {якщо комірка існує:} Begin {задаємо тип комірки:} Self. CurHeadCol [Self. CurGridSolveRow].ElmType:=TypeForCell; End Else {якщо в стовпці-заголовку немає такої комірки:} Begin ReportUnsupportedCell; Exit; End; End Else {якщо комірка у таблиці коефіцієнтів або правіше чи нижче неї:} Begin ReportUnsupportedCell; Exit; End; {Якщо тип комірки змінено, то перемальовуємо екранну таблицю для відображення нового типу комірки:} IfSelf. CurGrid<>Nil then Self. CurGrid. Invalidate; End; Procedure TGridFormattingProcs. AddCellTypeItemToMenu (SMenu:TPopupMenu; SCaption: String; IsCurrentItem: Boolean; SAssocType:THeadLineElmType; ToSetReactOnClick: Boolean=True); {Додає пункт меню для вибору типу комірки в таблиці з заданим написом SCaption і кругом того кольору, що асоційований з даним типом SAssocType . Для нового пункту меню настроює виклик процедури обробки комірки для задавання їй обраного типу SAssocType . Значення SAssocType записує у поле Tag об'єкта пункту меню. Вхідні дані: SMenu – контекстне меню для комірки, що формується; SCaption – підпис для пункту меню (назва типу комірки); IsCurrentItem – ознака того, що даний пункт меню має бути поточним (ввімкненим, відміченим) – що це поточний тип комірки; SAssocType – тип комірки, що прив'язаний до цього пункта меню, і буде присвоєний комірці при виборі цього пункту; ToSetReactOnClick – вмикач настройки виклику процедури задавання нового типу комірки (при виборі елемента меню). При ToSetReactOnClick = False це не виконується, і натискання елемента меню не викликає ніяких дій.} Var CurMenuItem:TMenuItem; SAssocColor:TColor; Begin If SMenu=Nil then Exit; {якщо меню не задано – елемент не додаємо в нього} {Створюємо новий тункт меню:} CurMenuItem:=TMenuItem. Create(Application); {Отримуємо колір для даного типу комірки:} SAssocColor:=Self. GetColorByElmType(SAssocType); {Біля тексту малюємо круг такого кольору, який асоційований з типом комірки, і буде присвоєний їй у разі вибору цього пунтку меню:} CurMenuItem. Bitmap. Height:=bc_MenuItemColorCircleDiameter; CurMenuItem. Bitmap. Width:=bc_MenuItemColorCircleDiameter; CurMenuItem. Bitmap. Canvas. Pen. Color:=SAssocColor; CurMenuItem. Bitmap. Canvas. Brush. Color:=SAssocColor; CurMenuItem. Bitmap. Canvas. Ellipse (CurMenuItem. Bitmap. Canvas. ClipRect); {0 – картинка задана у самому об'єкті, а не в SMenu . Images :} CurMenuItem. ImageIndex:=0; CurMenuItem. RadioItem:=True; {промальовувати перемикач, якщо не буде картинки} {Текст пункту меню:} CurMenuItem. Caption:=SCaption; CurMenuItem. Checked:=IsCurrentItem; If ToSetReactOnClick then {якщо обробка вибору елемента меню ввімкнена} Begin {Тип для комірки у випадку вибору цього пунтку меню:} CurMenuItem. Tag:=Integer(SAssocType); {Процедура-обробник вибору пункта меню:} CurMenuItem. OnClick:=Self. ProcOnCellTypeSelInMenu; CurMenuItem. AutoCheck:=True; End; SMenu. Items. Add(CurMenuItem); End; (* {Ідентифікатор для типу елемента масиву чисел та імен змінних. Типи змінних: залежні, незалежні, функції (умови-нерівності). Залежні змінні – це змінні, для яких діє умова невід'ємності:} THeadLineElmType=(bc_IndependentVar, bc_DependentVar, bc_FuncVal, bc_Number, bc_DestFuncToMax);} *) procedure TGridFormattingProcs. EdLineTaskOnMouseUp (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); {Процедура реагує на відпускання правої кнопки миші на комірках рядка-заголовка та стовпця-заголовка таблиці. Формує та відкриває контекстне меню для вибору типу комірки із можливих типів для цієї комірки.} Constsc_CurProcName='EdLineTaskOnMouseUp'; Var CurCol, CurRow, ArrayRow, ArrayCol: Integer; CurElmType:THeadLineElmType; MouseScrCoords:TPoint; Begin {Якщо до вмикання форматування був якийсь обробник події, запускаємо його:} If @Self. OldOnMouseUp<>Nil then Self. OldOnMouseUp (Sender, Button, Shift, X, Y); If Sender=Nil then Exit; {Якщо задано екранну таблицю даного об'єкта TGridFormattingProcs:} If Sender = Self. CurGrid then Begin If Button=mbRight then {якщо була відпущена права кнопка миші} Begin {Пробуємо узнати, на яку комірку натиснула миша:} CurCol:=-1; CurRow:=-1; Self. CurGrid. MouseToCell (X, Y, CurCol, CurRow); MouseScrCoords:=Self. CurGrid. ClientToScreen (Point(X, Y)); {Координати комірки у масивах таблиці і її заголовків:} ArrayRow:=CurRow-Self.CHeadRowNum-bc_LTaskRowsBeforeVars; ArrayCol:=CurCol-Self.CHeadColNum-bc_LTaskColsBeforeVars; {Якщо натиснуто на комірку рядка-заголовка:} If (CurRow=Self.CHeadRowNum) and (ArrayCol>=0) and (ArrayCol<Length (Self. CurHeadRow)) then Begin {очищаємо меню перед заповненням:} Self. InitGridPopupMenu (Self. CurGrid); {Якщо в екранній таблиці були зміни з часу останнього її читання, то читаємо комірку, для якої треба сформувати меню:} If Self. CurGridModified then Self. ReadHeadRowCell(ArrayCol); {Читаємо поточний тип комірки:} CurElmType:=Self. CurHeadRow[ArrayCol].ElmType; {Додаємо пункти меню:} {Якщо в комірці число-то тип комірки може бути тільки числовий:} If CurElmType=bc_Number then Self. AddCellTypeItemToMenu (Self. CurGrid. PopupMenu, sc_ValInHeadColOrRow, True, CurElmType) Else{якщо в комірці не число:} Begin {незалежна змінна:} Self. AddCellTypeItemToMenu (Self. CurGrid. PopupMenu, sc_IndependentVar, CurElmType = bc_IndependentVar, bc_IndependentVar); {залежна змінна:} Self. AddCellTypeItemToMenu (Self. CurGrid. PopupMenu, sc_DependentVar, CurElmType = bc_DependentVar, bc_DependentVar); End; End Else If (CurCol=Self.CHeadColNum) and (ArrayRow>=0) and (ArrayRow<Length (Self. CurHeadCol)) then Begin {якщо натиснуто на комірку стовпця-заголовка:} Self. InitGridPopupMenu (Self. CurGrid); {Якщо в екранній таблиці були зміни з часу останнього її читання, то читаємо комірку, для якої треба сформувати меню:} If Self. CurGridModified then Self. ReadHeadColCell(ArrayRow); {Читаємо поточний тип комірки:} CurElmType:=Self. CurHeadCol[ArrayRow].ElmType; {Додаємо пункти меню:} {Якщо в комірці число-то тип комірки може бути тільки числовий:} If CurElmType=bc_Number then Self. AddCellTypeItemToMenu (Self. CurGrid. PopupMenu, sc_ValInHeadColOrRow, True, CurElmType) Else{якщо в комірці не число:} Begin {назва фінкції – рядка нерівності:} Self. AddCellTypeItemToMenu (Self. CurGrid. PopupMenu, sc_InequalFuncName, CurElmType = bc_FuncVal, bc_FuncVal); {назва функції мети, що максимізується:} Self. AddCellTypeItemToMenu (Self. CurGrid. PopupMenu, sc_DestFuncToMaxName, CurElmType = bc_DestFuncToMax, bc_DestFuncToMax); {назва функції мети, що мінімізується:} Self. AddCellTypeItemToMenu (Self. CurGrid. PopupMenu, sc_DestFuncToMinName, CurElmType = bc_DestFuncToMin, bc_DestFuncToMin); End; End Else {якщо для даної комірки вибір типу не передбачено} Begin{ставимо в меню координати комірки (щоб користувач взагалі помітив, що меню є…)} Self. InitGridPopupMenu (Self. CurGrid); Self. AddCellTypeItemToMenu (Self. CurGrid. PopupMenu, sc_Row+sc_DoubleSpot+sc_Space+IntToStr (ArrayRow+1)+sc_KrKm+ sc_Space+sc_Col+sc_DoubleSpot+sc_Space+IntToStr (ArrayCol+1), True, bc_OtherType); End; {Записуємо координати комірки для обробника вибору типу з меню:} Self. CurGridSolveCol:=ArrayCol; Self. CurGridSolveRow:=ArrayRow; {Відображаємо меню:} Self. CurGrid. PopupMenu. Popup (MouseScrCoords.X, MouseScrCoords.Y); End; {If Button=mbRight then…} End {If Sender = Self. CurGrid then…} Else {якщо обробник викликала «чужа» таблиця або невідомий об'єкт:} Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_UnknownObjectCall+ sc_DoubleQuot+Sender. ClassName+sc_DoubleQuot); End; End; procedure TGridFormattingProcs. ReactOnSetEditText (Sender: TObject; ACol, ARow: Longint; const Value: string); {Процедура для реагування на редагування вмісту комірок під час редагування вхідних даних. Встановлює прапорець CurGridModified := True про те, що екранна таблиця має зміни.} Begin {Старий обробник теж викликаємо, якщо він є:} If @Self. OldOnSetEditText<>Nil then Self. OldOnSetEditText (Sender, ACol, ARow, Value); Self. CurGridModified:=True; End; Procedure TGridFormattingProcs. SetNewState (Value:TTableFormatState); Const sc_CurProcName='SetNewState'; Var StateSafe:TTableFormatState; OldHColPos, OldHRowPos: Integer; {Процедура для зміни режиму форматування GrowingStringGrid} Procedure GoSolveLTask; Begin {Вирішування задачі ЛП симплекс-методом:} CurGrid. ColCount:=bc_FixedCols+1; CurGrid. RowCount:=bc_FixedRows+1; CurGrid. FixedRows:=bc_FixedRows; CurGrid. FixedCols:=bc_FixedCols; If Not (Self. PrepareToSolveLTask) then Begin {Якщо не вдається підготувати таблицю до вирішування задачі:} StateSafe:=Self. CurFormatState; {Перемикаємо на режим fs_NoFormatting, і назад у поточний, щоб встановити усі настройки цього режиму (повернутися до них):} Self. TableFormatState:=fs_NoFormatting; Self. TableFormatState:=StateSafe; Exit; End; CurGrid. OnNewCol:=NumerationOnNewCol; CurGrid. OnNewRow:=NumerationOnNewRow; CurGrid. OnDrawCell:=EdLineTaskOnDrawCell; CurGrid. OnDblClick:=OldOnDblClick; CurGrid. OnMouseUp:=OldOnMouseUp; CurGrid. OnSetEditText:=OldOnSetEditText; {Вимикаємо редагування екранної таблиці:} CurGrid. Options:=CurGrid. Options – [goEditing]; End; Begin If InSolving then Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (sc_CurProcName+sc_CantChangeStateInSolving); Exit; End; If Self. CurGrid=Nil then {Якщо екранну таблицю не задано:} Begin{запам'ятовуємо поточний режим, і більше нічого не робимо тут:} Self. CurFormatState:=Value; Exit; End; {Якщо задано новий режим:} IfSelf. CurFormatState<>Valuethen Begin{Якщо форматування було вимкнене:} If Self. CurFormatState=fs_NoFormatting then Begin {Запам'ятовуємо обробники подій, які замінимо на свої форматувальники:} OldOnNewCol:=CurGrid. OnNewCol; OldOnNewRow:=CurGrid. OnNewRow; OldOnDrawCell:=CurGrid. OnDrawCell; OldOnDblClick:=CurGrid. OnDblClick; OldOnSetEditText:=CurGrid. OnSetEditText; OldOnMouseUp:=CurGrid. OnMouseUp; End; {Якщо таблиця редагована, то приймаємо останні зміни перед зміною режиму:} If Self. CurGridModified then Self. Refresh; Case Value of fs_EnteringEqs: {редагування таблиці системи лінійних рівнянь:} Begin {Встановлюємо потрібну кількість рядків і стовпців екранної таблиці для фіксованих заголовків («тільки для читання»). Для цього забезпечуємо щоб кількість рядків і стовпців не була меншою за потрібну кількість фіксованих, плюс хоч один стовпець / рядок (хоч одна комірка) для редагування:} If CurGrid. ColCount<bc_FixedCols+1 then CurGrid. ColCount:=bc_FixedCols+1; If CurGrid. RowCount<bc_FixedRows+1 then CurGrid. RowCount:=bc_FixedRows+1; CurGrid. FixedRows:=bc_FixedRows; CurGrid. FixedCols:=bc_FixedCols; {Позиціювання таблиці до зміни режиму:} OldHColPos:=Self.CHeadColNum; OldHRowPos:=Self.CHeadRowNum; {Позиціювання відображення таблиці у даному режимі редагування:} Self.CHeadColNum:=CurGrid. FixedCols-1; Self.CHeadRowNum:=CurGrid. FixedRows-1; {Якщо позиціювання змінилося, то відображаємо таблицю в новому місці:} If (OldHColPos<>Self.CHeadColNum) or (OldHRowPos<>Self.CHeadRowNum) then Self. Refresh; CurGrid. OnNewCol:=EditLineEqsOnNewCol; CurGrid. OnNewRow:=EditLineEqsOnNewRow; CurGrid. OnDrawCell:=EditLineEqsOnDrawCell; CurGrid. OnDblClick:=OldOnDblClick; CurGrid. OnMouseUp:=OldOnMouseUp; {Вмикаємо можливість редагування:} CurGrid. Options:=CurGrid. Options+[goEditing]; CurGrid. OnSetEditText:=ReactOnSetEditText; InSolving:=False; End; fs_EnteringLTask: Begin {Редагування таблиці задачі ЛП (максимізації/мінімізації):} {Встановлюємо потрібну кількість рядків і стовпців екранної таблиці для фіксованих заголовків («тільки для читання»). Для цього забезпечуємо щоб кількість рядків і стовпців не була меншою за потрібну кількість фіксованих, плюс хоч один стовпець / рядок (хоч одна комірка) для редагування:} If CurGrid. ColCount<bc_FixedCols+1 then CurGrid. ColCount:=bc_FixedCols+1; If CurGrid. RowCount<bc_FixedRows+1 then CurGrid. RowCount:=bc_FixedRows+1; CurGrid. FixedRows:=bc_FixedRows; CurGrid. FixedCols:=bc_FixedCols; {Позиціювання таблиці до зміни режиму:} OldHColPos:=Self.CHeadColNum; OldHRowPos:=Self.CHeadRowNum; {Позиціювання відображення таблиці у даному режимі редагування:} Self.CHeadColNum:=CurGrid. FixedCols-1 + bc_LTaskColsBeforeVars; Self.CHeadRowNum:=CurGrid. FixedRows-1; {Якщо позиціювання змінилося, то відображаємо таблицю в новому місці:} If (OldHColPos<>Self.CHeadColNum) or (OldHRowPos<>Self.CHeadRowNum) then Self. Refresh; CurGrid. OnNewCol:=EdLineTaskOnNewCol; CurGrid. OnNewRow:=EdLineTaskOnNewRow; CurGrid. OnDrawCell:=EdLineTaskOnDrawCell; CurGrid. OnDblClick:=EdLineTaskOnDblClick; CurGrid. OnMouseUp:=EdLineTaskOnMouseUp; {Вмикаємо можливість редагування:} CurGrid. Options:=CurGrid. Options+[goEditing]; CurGrid. OnSetEditText:=ReactOnSetEditText; InSolving:=False; End; fs_SolvingEqsM1: {вирішування системи лінійних рівнянь способом 1:} Begin CurGrid. ColCount:=bc_FixedCols+1; CurGrid. RowCount:=bc_FixedRows+1; CurGrid. FixedRows:=bc_FixedRows; CurGrid. FixedCols:=bc_FixedCols; {Пробуємо підготувати таблицю до вирішування. Якщо не вдається, то залишаємось у режимі, який був до спроби його змінити:} If Not (Self. PrepareToSolveEqsWithM1) then Begin StateSafe:=Self. CurFormatState; {Перемикаємо на режим fs_NoFormatting, і назад у поточний, щоб встановити усі настройки цього режиму:} Self. TableFormatState:=fs_NoFormatting; Self. TableFormatState:=StateSafe; Exit; End; CurGrid. OnNewCol:=NumerationOnNewCol; CurGrid. OnNewRow:=NumerationOnNewRow; CurGrid. OnDrawCell:=SolveLineEqsM1OrM2OnDrawCell; CurGrid. OnDblClick:=OldOnDblClick; CurGrid. OnMouseUp:=OldOnMouseUp; {Вимикаємо редагування екранної таблиці:} CurGrid. Options:=CurGrid. Options – [goEditing]; CurGrid. OnSetEditText:=OldOnSetEditText; End; fs_SolvingEqsM2: {вирішування системи лінійних рівнянь способом 2:} Begin CurGrid. ColCount:=bc_FixedCols+1; CurGrid. RowCount:=bc_FixedRows+1; CurGrid. FixedRows:=bc_FixedRows; CurGrid. FixedCols:=bc_FixedCols; {Пробуємо підготувати таблицю до вирішування. Якщо не вдається, то залишаємось у режимі, який був до спроби його змінити:} If Not (Self. PrepareToSolveEqsWithM2) then Begin StateSafe:=Self. CurFormatState; {Перемикаємо на режим fs_NoFormatting, і назад у поточний, щоб встановити усі настройки цього режиму:} Self. TableFormatState:=fs_NoFormatting; Self. TableFormatState:=StateSafe; Exit; End; CurGrid. OnNewCol:=NumerationOnNewCol; CurGrid. OnNewRow:=NumerationOnNewRow; CurGrid. OnDrawCell:=SolveLineEqsM1OrM2OnDrawCell; CurGrid. OnDblClick:=OldOnDblClick; CurGrid. OnMouseUp:=OldOnMouseUp; CurGrid. OnSetEditText:=OldOnSetEditText; {Вимикаємо редагування екранної таблиці:} CurGrid. Options:=CurGrid. Options – [goEditing]; End; fs_SolvingLTask: GoSolveLTask; fs_FreeEdit: {Режим вільного редагування таблиці:} Begin CurGrid. OnNewCol:=OldOnNewCol; CurGrid. OnNewRow:=OldOnNewRow; CurGrid. OnDrawCell:=OldOnDrawCell; CurGrid. OnDblClick:=OldOnDblClick; CurGrid. OnMouseUp:=OldOnMouseUp; {Вмикаємо редагування екранної таблиці:} CurGrid. Options:=CurGrid. Options+[goEditing]; {Вмикаємо стеження за змінами в екнанній таблиці:} CurGrid. OnSetEditText:=ReactOnSetEditText; InSolving:=False; End; Else {Без форматування (fs_NoFormatting), або невідомий режим:} Begin CurGrid. OnNewCol:=OldOnNewCol; CurGrid. OnNewRow:=OldOnNewRow; CurGrid. OnDrawCell:=OldOnDrawCell; CurGrid. OnDblClick:=OldOnDblClick; CurGrid. OnMouseUp:=OldOnMouseUp; CurGrid. OnSetEditText:=OldOnSetEditText; InSolving:=False; End; End; CurGrid. Invalidate; {перемальовуємо таблицю з новими форматувальниками} Self. CurFormatState:=Value; {запам'ятовуємо новий режим форматування} End; End; Procedure TGridFormattingProcs. SetNewGrid (Value:TGrowingStringGrid); Var SafeFormatState:TTableFormatState; Begin If Self. CurGrid<>Value then {якщо задано новий об'єкт таблиці:} Begin SafeFormatState:=Self. TableFormatState; {Знімаємо усі процедури-форматувальники, перемальовуємо таблицю (якщо вона була) перед заміною її на задану:} Self. TableFormatState:=fs_NoFormatting; Self. CurGrid:=Value; {запам'ятовуємо вказівник на новий об'єкт таблиці} {Застосовуємо форматування для нової таблиці (якщо вона не відсутня, вказівник на неї не рівний Nil ):} Self. TableFormatState:=SafeFormatState; Self. Refresh; End; End; Procedure TGridFormattingProcs. SetHeadColNum (Value: Integer); Begin If Self. CurFormatState=fs_FreeEdit then Begin If Value<0 then Value:=0; Self.CHeadColNum:=Value; End; End; Procedure TGridFormattingProcs. SetHeadRowNum (Value: Integer); Begin If Self. CurFormatState=fs_FreeEdit then Begin If Value<0 then Value:=0; Self.CHeadRowNum:=Value; End; End; Procedure TGridFormattingProcs. SetNewMemo (Value:TMemo); Begin If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (Self. ClassName+': повідомлення вимкнені.'); Self. CurOutConsole:=Value; If Self. CurOutConsole<>Nil then Self. CurOutConsole. Lines. Add (Self. ClassName+': повідомлення ввімкнені.'); End; end. лінійний програмування компромісний розв'язок Хоч кожній залежній змінній одної задачі відповідає функція-умова (нерівність) двоїстої, і кожній функції-умові відповідає залежна змінна, ці пари величин приймають різні значення у розв’язку пари задач. Компромісний розв’язок багатокритеріальної задачі ЛП зручно застосовувати для об’єктів управління з такими вихідними параметрами (функціями мети), які є практично рівноправними (мають однаковий пріоритет до оптимізації, або їх пріоритети складно оцінити). За допомогою нього можна отримати розв’язок з мінімальним сумарним програшем оптимізації параметрів. 1. Левин С.В., Александрова В.В.: «БАГАТОКРИТЕРІАЛЬНА ОПТИМІЗАЦІЯ З ВИКОРИСТАННЯМ ТЕОРЕТИКО-ІГРОВОГО ПІДХОДУ»: методичні вказівки до виконання курсової роботи з курсу «Математичні методи дослідження операцій» – Харків, Національний аерокосмічний університет ім. М.Є. Жуковського «Харківський авіаційний інститут», 2008 р. 2. Довідка з Borland Delphi 6. |