Главная Рефераты по рекламе Рефераты по физике Рефераты по философии Рефераты по финансам Рефераты по химии Рефераты по хозяйственному праву Рефераты по цифровым устройствам Рефераты по экологическому праву Рефераты по экономико-математическому моделированию Рефераты по экономической географии Рефераты по экономической теории Рефераты по этике Рефераты по юриспруденции Рефераты по языковедению Рефераты по юридическим наукам Рефераты по истории Рефераты по компьютерным наукам Рефераты по медицинским наукам Рефераты по финансовым наукам Рефераты по управленческим наукам Психология и педагогика Промышленность производство Биология и химия Языкознание филология Издательское дело и полиграфия Рефераты по краеведению и этнографии Рефераты по религии и мифологии Рефераты по медицине Рефераты по сексологии Рефераты по информатике программированию Краткое содержание произведений |
Курсовая работа: Багатокритеріальна задача лінійного програмуванняКурсовая работа: Багатокритеріальна задача лінійного програмуванняРозв’язати багатокритеріальну задачу лінійного програмування з отриманням компромісного розв’язку за допомогою теоретико-ігрового підходу. Задача (варіант 1): Z1= x1+2x2+x3 ® max Z2= – x1 –2x2+x3+x4 ® min Z3= –2x1 –x2+x3+x4 ® max з обмеженнями 2x1 –x2+3x3+4x4 £ 10 x1+x2+x3 –x4 £ 5 x1+2x2 –2x3+4x4 £ 12 "x ³ 0 У цій роботі реалізовано вирішування таких задач лінійного програмування: розв’язування задач багатокритеріальної оптимізації, тобто пошук компромісного рішення для задач з кількома функціями мети. Ця задача така: Задано об’єкт управління, що має n входів і k виходів. Вхідні параметри складають вектор X = {xj}, . Кожен з вхідних параметрів може мати обмеження, що накладене на область його значень. В програмі підтримуються параметри без обмежень на значення, і з обмеженнями невід’ємності (з областю ). Також на комбінації вхідних значень можуть бути накладені обмеження як система лінійних рівнянь або нерівностей: Вихідні сигнали об’єкта є лінійними комбінаціями вхідних сигналів. Для досягнення ефективності роботи об’єкта управління частину вихідних сигналів треба максимізувати, інші – мінімізувати, змінюючи вхідні сигнали і дотримуючись обмежень на ці сигнали (задоволення усіх нерівностей, рівнянь і обмежень області значень кожного з вхідних параметрів). Тобто вихідні сигнали є функціями мети від вхідних: Як правило, для багатокритеріальної задачі не існує розв’язку, який би був найкращим (оптимальним) для усіх функцій мети одночасно. Проте можна підібрати такий розв’язок, який є компромісним для усіх функцій мети (в точці цього розв’язку кожна з функцій мети якнайменше відхиляється від свого оптимального значення в заданій системі умов (обмежень). Тут реалізовано пошук компромісного розв’язку за допомогою теоретико-ігрового підходу, що був розроблений під керівництвом доцента ХАІ Яловкіна Б.Д. Цей підхід дозволяє знайти компромісний розв’язок з мінімальним сумарним відхиленням всіх виходів (значень функцій мети) від їхніх екстремальних значень за даної системи обмежень. Йде пошук компромісного вектора значень змінних в такому вигляді: тут – вектор, що оптимальний для i-го критерію (функції мети); li – вагові коефіцієнти. Для отримання цього вектора виконуються такі кроки розв’язування: 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={C1X, …, CkX} другого. Всі міри неоптимальності є недодатними, і є коефіцієнтами програшу першого гравця. На головній діагоналі вони рівні нулю (бо є мірами неоптимальності оптимального вектора для своєї ж функції). 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 Функцію Z2, що мінімізується, замінили на протилежну їй – Z2, що максимізується. Запис для вирішування симплекс-методом максимізації Незалежних змінних немає. 0-рядків немає. Опорний розв’язок: готовий. Пошук оптимального:
Після отримання розв’язку максимізації для – Z2, взято протилежну до неї функцію Z2, і отримано розв’язок мінімізації для неї Результат для прямої задачі: У рядку-заголовку: – 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=array of TValOrName; {тип масиву для заголовків матриці} TFloatArr=array of TWorkFloat; {тип масиву дійсних чисел} 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 і заповнює нові елементи значеннями за змовчуванням. Використовується при зміні розмірів екранної таблиці. Після її виклику можна вказувати типи змінних у рядку-заголовку (користувач вибирає залежні та незалежні):} Procedure UpdateLTaskHeadRowToStrGrid (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), то створює новий. Видаляє усі пунтки (елементи, теми) з меню:} Procedure InitGridPopupMenu (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:} Procedure ReadHeadRowCell (SCol: Integer); {Зчитує комірку з екранної таблиці в стовпець-заголовок. Вхідні дані: SRow – номер комірки у стовпці-заголовку. Для екранної таблиці використовуються координати комірки відповідно до координат рядка-заголовка та стовпця заголовка (верхнього лівого кута таблиці з заголовками): HeadColNumInGrid і HeadRowNumInGrid:} Procedure ReadHeadColCell (SRow: Integer); {Процедура для зчитування таблиці та її заголовків із CurGrid:} Function ReadTableFromGrid: 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:} Function SolveEqsWithM1: Boolean; {Вирішування системи лінійних рівнянь способом 2:} Function SolveEqsWithM2: 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; {Вирішування задачі багатокритеріальної оптимізації лінійної форми з використанням теоретико-ігрового підходу. Умовою задачі є умови-нерівності, рівняння та умови на невід'ємність окремих змінних, і декілька функцій мети, для яких треба знайти якомога більші чи менші значення. Функція повертає ознаку успішності вирішування:} Function SolveMultiCritLTask: 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 default Nil; {Поле для відображення повідомлень:} Property MemoForOutput:TMemo read CurOutConsole write SetNewMemo default Nil; {Номери стовпця і рядка-заголовків у 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):} Function MakeDualLTask: 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 – стовпець-заголовок таблиці; Row1, Row2 – рядки, що треба поміняти місцями; 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 – рядок-заголовок таблиці; Row1, Row2 – рядки, що треба поміняти місцями; 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>=<a11>*<ряд1> + <a12>*<ряд2> +… + <a1n>*<рядn>; … <стовпm>=<am1>*<ряд1> + <am2>*<ряд2> +… + <amn>*<рядn>; І підраховує значення, якщо можливо: <стовп1>=<значення1>; … <стовпm>=<значенняm>} Var CurRowN, 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; Procedure ShowResultCalc; {Відображає записи значень змінних (у текстовому полі) такого зказка: <стовп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 {Зчитуємо таблицю. Як рядок-заголовок зчитуємо автоматично сформовані назви змінних x1…xn та множник вільних членів (1). Як стовпець-заголовок зчитуємо стовпець нумерації. При переході до режиму вирішування задачі у цей стовпець будуть скопійовані вільні члени (режим способу 1, fs_SolvingEqsM1), або нулі (режим способу 2, fs_SolvingEqsM2):} 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; {Збільшуємо розміри матриці до квадратних:} If CurWidth>CurHeight then {Якщо ширина була більша за висоту:} Begin SetLength (SDMatrix, CurWidth, CurWidth); {збільшуємо висоту} End Else if CurWidth<CurHeight then {Якщо висота була більша за ширину:} 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). За правилом двоїсту задачу потрібно мінімізувати, якщо для прямої потрібно було знайти максимум, і максимізувати, якщо для прямої потрібно було знайти мінімум. } Const sc_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», і заголовок для неї був додатний), тому змінюємо знак заголовка:} If DualTType=dt_MaxToMin then 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 {Проходимо по усіх елементах-заголовках рядків, і останньому елементу – заголовку рядка функції мети:} If Self. CurHeadCol[CurRow].ElmType=bc_FuncVal then {Якщо нерівність «<=»:} 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 (окрім вільгого члена), бо помножили і усі x1…xn на -1. Якщо треба мінімум, то ці коефіцієнти не множимо (бо x1…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-го стовпця матриці неоптимальності. Вихідні дані: міра неоптимальності.} Var VarNum: 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=- 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 – матриця коефіцієнтів умови багатокритеріальної лінійної задачі оптимізації. Вихідні дані: Повертає значення функції мети.} Var VarNum: 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; {Якщо таких рядків не знайдено, то усі вони вже вгорі:} If CurRowToUp<=CurRowNum then Break 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 – таблиця коефіцієнтів і стовпець-заголовок з перенесеними донизу рядками і комірками; функція повертає номер найвищого рядка із тих, що переміщені вниз (вище нього – рядки тих типів, що не було задано переміщувати донизу).} Var AllOtherHeadTypes: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 {якщо рядки для виключення ще залишились:} {Шукаємо ненульову комірку серед коефіцієнтів поточної незалежної змінної (окрім останнього рядка, що є рядком поточної функції мети):} If SearchNozeroSolveCell (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 x1 x2 x3 x4 x5… 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 у режимі набору вхідних даних системи лінійних рівнянь. Зафарбовує в інший колір останній стовпець – стовпець правих частин рівнянь.} Var CurGrid: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 y1 2 y2 3 y3 4 y4 5 y5 … 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 x1 x2 x3 x4… 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; {Останній елемент є числом, а не змінною: це множник стовпця вільних членів (правих частин):} If CurLTaskVarCount>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); {Обробник вибору пункту в меню типів для комірки рядка – чи стовпця-заголовка.} Const sc_CurProcName='ProcOnCellTypeSelInMenu'; Procedure ReportUnsupportedCell; 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; {Якщо тип комірки змінено, то перемальовуємо екранну таблицю для відображення нового типу комірки:} If Self. 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); {Процедура реагує на відпускання правої кнопки миші на комірках рядка-заголовка та стовпця-заголовка таблиці. Формує та відкриває контекстне меню для вибору типу комірки із можливих типів для цієї комірки.} Const sc_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; {Якщо задано новий режим:} If Self. CurFormatState<>Value then 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. |
||||||||||||||||||||||||||||||||||||||||||||||||||
|