Крысолов : другие произведения.

Технология разработки простейших приложений

Самиздат: [Регистрация] [Найти] [Рейтинги] [Обсуждения] [Новинки] [Обзоры] [Помощь|Техвопросы]
Ссылки:


 Ваша оценка:
  • Аннотация:
    Это для начинающих программистов и студентов, изучающих основы программирования. С удивлением обнаружил, что эту элементарщину, почему-то не дают. Программа, писалась в Дельфи как приложение для винды.


   В этом тексте будет показана самая примитивная технология разработки прикладных простейших программ.
   Так как надобность возникла написать программу для генерации заданий для небольшой контрольной работы по Линейной Алгебре (решение систем уравнений методом Гаусса, Крамера, обратной матрицы), то и технологию буду показывать пошагово, как сам буду ту прогу писать.
   (прим. Да, я мог бы ещё поставить в прогу менюху непосредственного вывода на печать, но как показывает опыт, лучше иметь "предварительный" вариант, который можно перерасчитать многажды и после скопипастить нужный кусок в итоговый док. Что выйдет и легче и намного продуктивнее. Ведь генерятся варианты значений коэффициентов рандомно. И там может получаться очень тяжёлая для вычисления система уравнений. Или наоборот -- слишком холявная.)
  
  Сразу, главное:
   Вся разработка программы должна быть поделена на мелкие, ЭЛЕМЕНТАРНЫЕ шаги, после каждого из которых вы можете откомпилировать программу и проверить работает ли она = работает ли, правильно ли написан тот МАЛЕНЬКИЙ фрагмент, что вы только что написали.
  
  
  
   Самый первый шаг -- разработка интерфейса
  
   Т.е. Как прога будет выглядеть на экране пользователя.
   Вообще -- чем проще, тем лучше. А чтобы пользователь не гадал о том, что он таки открыл (случайно) на форме должно отображаться название проги, говорящее хотя бы приблизительно о том, что же она такого выполняет.
   Примерно вот так:
    []
   Название, как видите, "Рисую матрицы".
  
   Для элемента memo1 включены обе рейки прокрутки. Можно и вертикальной обойтись, но это так -- на всякий случай. Типа "не помешает", и если что-то прикручивать придётся по ходу дела не будет голова болеть за то, что что-то элементарное забыл сделать.
   Также для memo1 свойство font.size установил на 14. Можно было, конечно, вывести на панель и выбор шрифта, и кегля для него, но посчитал излишним. Всё равно текст копипастой будет переноситься из Мемо в Ворд (или как у меня -- в ОпенОфисРайтер). А там возможностей форматирования текста -- гораздо больше, чем можно быстро написать в кодах для элементарной подсобной программы. Так что в этом я соблюдаю принцип разумной достаточности.
  
   Правая кнопка типа Битбаттон. На ней включено свойство Kind на Close. Так не нужно писать отдельно обработчик на кнопку -- и так будет срабатывать на закрытие проги.
   Левая кнопка -- из обычных. Надпись на ней показывает что она предназначена для запуска процесса вычисления матриц и рисования их в memo1.
  
   Когда слеплен интерфейс, стоит навесить чего-то в виде кода на исполняющую кнопку. Чтобы можно было посмотреть как оно будет выглядеть и как работать.
   Таким образом первый шаг в коде, в файле Unit1.pas будет выглядеть так:
   unit Unit1;
  
   interface
  
   uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, Buttons, StdCtrls, ExtCtrls;
  
   type
   TForm1 = class(TForm)
   Memo1: TMemo;
   Panel1: TPanel;
   Btn2: TButton;
   btn1: TBitBtn;
   procedure Btn2Click(Sender: TObject);
   private
   { Private declarations }
   public
   { Public declarations }
   end;
  
   var
   Form1: TForm1;
  
   implementation
  
   {$R *.dfm}
  
   procedure TForm1.Btn2Click(Sender: TObject);
   var
   N :Integer;
   begin
   N:=1;
   Memo1.Lines.Add('Вариант '+inttostr(N));
   end;
  
   Нажатие кнопки "выполнить" тем самым приведёт к появлению надписи: "Вариант 1". В сущности, завершённая прога. Но не для вычисления а так -- что-то показать.
   Надо это делать для того, чтобы после "не искать ёжика в тумане" - ошибки сделанные на давно пройденных этапах.
   И да: N - вводится как ЛОКАЛЬНАЯ переменная.
   Зачем?
   Да для того, чтобы после, при очередном нажатии кнопки "Вычислить" исчисление вариантов шло по-новой. С первого. Таким образом можно делать неограниченное кол-во вариантов и контрольных.
   Для этого хорошо вводить циклы типа For. Он жёстко задаёт кол-во итераций, и нет опасности зацикливания. Посавим "для тренировки" диапазон значений N для цикла от 1 до 30. Откомпилируем файл.
   Получим:
   []
   Но тут возникает идея, что стоило бы сделать так ,чтобы пользователь, без перекомпиляции исполняемого файла мог бы сам задавать кол-во этих вариантов. Хорошо! Вводим элемент SpinEdit на панель.
   На вкладышах Delphi он находится в Samples.
   Чтобы было понятно, для чего сей элемент, рядом ставим элемент Label чтобы отображал надпись "Потребное кол-во вариантов". В самом же SpinEdit свойство Value устанавливаем в 20. Т.е. Кол-во вариантов устанавливается им в 20 штук. Так как можно нащёлкать в нём любое кол-во от нуля до "скокозахочишь" - на откуп пользователю.
  
    []
  
   При этом стоит исправить слегка обработчик. Ведь ранее N задавался явно до цикла и цикл прокручивался до N. Теперь надо указать ,что цикл прокручивается до значения, указанного в свойстве Value элемента SpinEdit1 (чтобы сразу брал число, что пользователь наклацает).
   procedure TForm1.Btn2Click(Sender: TObject);
   var
   N :Integer;
   begin
  
   for N:=1 to SpinEdit1.Value do
   begin
   Memo1.Lines.Add('Вариант'+inttostr(N));
   end;
   end;
   (прим.: функция inttostr(N) переводит значение N типа Integer в строчный - String)
   После правки весьма полезно откомпилировать и посмотреть не закралась ли тут какая-нибудь мелкая ошибка. Заметьте: каждый раз мы доводим до какого-то этапа, когда программа является как-бы законченной. "Как-бы" - это в смысле "можно откомпилировать до исполняемого файла и получить какой-то результат".
   Т.е. Если будут "висеть сопли" из недописанного кода, то никакого исполняемого вы не получите, а получите ругань компилятора, что он там нашёл некие "ошибки" и не может сделать исполняемый файл (впрочем, может и скомпилировать, но ответ чаще всего будет несуразный) .
   Поэтому, если вы таки что-то наперёд написали, но оно не закончено, то обязательно, перед проверкой-компиляцией заключайте эти куски кода в фигурные скобки {} или на каждую строку ставьте "заглушку" в виде //. Т.е. Переводите эти куски в статус "комментариев" и компилятор их пропустит.
   Смысл этого приёма в том, что вы проверяете код кусками. И дальше, в случае появления ошибок, вам не нужно проверять то, что уже работало до.
   Кроме этого, если вы не поняли где в вашем блоке идёт ошибка, можно снимать // по отдельным операторам и строкам. Тогда будет видно на чём ваша прога начинает работать неправильно.
  
   Итак, по сути, разработка интерфейса практически завершена.
   Мы уже видим как примерно будет выводиться результат. И кусок кода, ответственный за этот вывод работает.
   Можно приступать к разработке кода, выводящего на печать сначала матрицы, а после и конкретные текстовые варианты систем уравнений.
  
  -- Шаг второй -- разработка кода вычисления матриц
   Тут стоило бы пояснить почему так -- "вычисления матриц".
   По сути, программа будет высчитывать и выводить на печать системы уравнений.
   Чтобы они были разными, нужно сделать так, чтобы коэффициенты генерировались случайным образом. Но тут возникает проблема: если определитель матрицы коэффициентов равен нулю, то система не имеет решений. Следовательно, надо отсечь от печати именно такие варианты.
   Отсюда, пишем код с генерацией конкретных коэффициентов функцией random, и проверяем получившийся набор "на паршивость". Все наборы, определитель которых равен нулю -- отбрасываем. Но чтобы соблюсти технологию, вся процедура у нас разбивается на следующие этапы.
  -- Первый этап -- генерация коэффициентов и вывод их на печать
   Последнее всё равно понадобится и подправить ту строку вывода достаточно просто если есть уже основа.
   Значит, первым делом объявляем двумерный массив: а: array [1..3,1..3] of Integer; так же локально в рамках процедуры, обрабатывающей нажатие кнопки "Вычислить".
   Аналогично объявляем массив b: array [1..3] of Integer; В нём будут содержаться числа чему равны каждое из уравнений системы. Да, на данном этапе они не используются, но если есть возможность копипастой продублировать строку и исправить пару символов -- почему бы и не?
   Вставлять циклы с генератором надо внутрь цикла пишущего номер варианта. Таким образом, для каждого нового варианта у нас будет генерироваться новый набор коэффициентов.
   Ясное дело, что надо бы проверить -- всё ли хорошо там присваивается. Значит, получающиеся коэффициенты весьма полезно вывести на печать и посмотреть что там в каждом: Memo1.Lines.Add('a11='+inttostr(a[1,1])+' a12='+inttostr(a[1,2])+' a13='+inttostr(a[1,3]));
   Обратите внимание на запись ' a12='. После открывающей кавычки вставлены два пробела. Также и для третьего элемента. Это служит для того, чтобы запись предыдущего не сливалась с последующей.
   (прим.: функция inttostr служит для перевода целого значения типа Integer в строковый тип).
   Итого наш код процедуры стал выглядеть так:
   procedure TForm1.Btn2Click(Sender: TObject);
   var
   N,NN,k,m :Integer;
   a: array [1..3,1..3] of Integer;
   b: array [1..3] of Integer;
   begin
  
   for N:=1 to SpinEdit1.Value do
   begin
   Memo1.Lines.Add('Вариант '+inttostr(N)); //выводит "вариант N"
   for k:=1 to 3 do
   for m:=1 to 3 do
   begin
   a[k,m]:=Random(21); // присваиваем случайные целые числа от 0 до 20 элементам массива
   end;
   Memo1.Lines.Add('a11='+inttostr(a[1,1])+' a12='+inttostr(a[1,2])+' a13='+inttostr(a[1,3])); //выводим коэффициенты первой строки;
   Memo1.Lines.Add('a21='+inttostr(a[2,1])+' a22='+inttostr(a[2,2])+' a23='+inttostr(a[2,3])); //выводим коэффициенты второй строки;
   Memo1.Lines.Add('a31='+inttostr(a[3,1])+' a32='+inttostr(a[3,2])+' a33='+inttostr(a[3,3])); //выводим коэффициенты третьей строки;
   end;
   end;
   результат работы программы на этом этапе:
    []
  
   Впрочем, после прикидки сложности вычисления определителей получаемых матриц, уменьшаю число выбора в рандоме до 10 (т. е. выбор будет от 0 до 9). Ибо неча студентов мучить! :-) (прим. В конце концов снизил до 4)
   Примечание: вывод коэффициентов на Мемо сейчас -- только для контроля результата. Т.е. Всё ли правильно делается и всё ли работает. Вполне естественно, что данный модуль вывода на Мемо впоследствии будет либо отключён скобками {}, либо существенно переработан уже для вывода полноценной системы уравнения.
  -- Второй этап: вычисление определителя |A|
   Тут уже просто: берём уже сгенерированные коэффициенты и загоняем их в стандартную формулу. Попутно загоняя в блок var объявления новых переменных.
   procedure TForm1.Btn2Click(Sender: TObject);
   var
   N,NN,A0,A1,A2,A3,k,m,l :Integer;
   a: array [1..3,1..3] of Integer;
   b: array [1..3] of Integer;
   s: array [1..3,2..3] of String;
   C: array [1..3,1..3] of Integer;
  
   begin
   Так как мне лень много писать, я в код загоняю формулу подсчёта определителя через миноры. А чоа? Ведь короче!
   Таким образом в коде добавляется:
   A:= a[1,1]*(a[2,2]*a[3,3]-a[2,3]*a[3,2])-a[2,1]*(a[1,3]*a[3,3]-a[1,3]*a[3,2])+a[3,1]*(a[1,2]*a[2,3]-a[2,2]*a[1,3]);
   Memo1.Lines.Add('A='+inttostr(A)); //выводим значение определителя А;
   Проверяем как эти строки работают.
   []
   Как видите, в Варианте 8 выскочило то, что ожидалось как неприятность, и которую надо отсекать: Определитель с данным набором оказался равен нулю. Т.е. "нет решений у системы".
   Прим. авт.: вышеприведённый фрагмент кода с вычислением определителя слегка неверный. Мелкая хохмочка: я много писал на С++ и иногда забываю, что в Паскале нет различия между объявлением переменной в виде "А" и "а"(в С++ это будут РАЗНЫЕ переменные). А в этой проге поймался на "сишных" стереотипах. Пришлось записи к А прибавлять нолик в объявлении типов после рыка компилятора, "redeclared 'a'" поэтому сейчас значение определителя заносится в переменную "А0". То, что в выводе на мемо не менял вид -- это уже мелочи. Так запись 'A=' ни что иное как просто строка символов никакого отношения не имеющая в коде к переменной А0 типа Integer).
   Вполне естественно, что наборы значений коэффициентов ведущих к появлению А=0 надо отсекать. Значит, при появлении такого значения, надо пересчитывать коэффициенты для данного варианта. Это значит, что здесь нужен небольшой цикл или оператор условия.
   Так как связка If - GO TO ныне моветон, используем цикл repeat - until(условие);
   Данный оператор выполняется хотя бы один раз, если выполняется условие и многажды, до тех пор, пока не будет выполнено условие. Т.е. Если значение условия False(в нашем случае А0=0) то он выполняется до тех пор, пока не примет значение True (т. е. А0 не ноль).
   repeat
   for k:=1 to 3 do
   for m:=1 to 3 do
   begin
   l:=Random(2);
   if l>0 then
   a[k,m]:=Random(10) //присваиваем положительные целые числа от 0 до 9 если l>0
   else a[k,m]:=(-1)*Random(10); //присваиваем положительные целые числа от 0 до 9 если l=0
   end;
   A0:= a[1,1]*(a[2,2]*a[3,3]-a[2,3]*a[3,2])-a[2,1]*(a[1,2]*a[3,3]-a[1,3]*a[3,2])+a[3,1]*(a[1,2]*a[2,3]-a[2,2]*a[1,3]);
   until((A0<0)or(A0>0));
  
   Прим: ясное дело, что выразить условие можно ещё двумя способами. Я вставил то, что первым на ум пришло. Бо "красиво смотрится". Так как это -- вопрос вкуса. :-)
   Как показывает тестовый прогон получившейся проги, А=0 больше не появляется. Чего и добивались.
  -- Третий этап: вычисление А1,А2,А3 и собственно решения системы уравнений
   Для этого во-первых, нужно сгенерить b1,b2,b3.
   Уж чего проще, так как код для этого уже есть. Остаётся только скопипастить блоком и поправить где надо.
   Чтобы то, что уже сделано и работает не мозолило глаза, закрываем скобками{} превращая временно в "комментарии".
   Memo1.Lines.Add('A='+inttostr(A0)); //выводим значение определителя А;
  
   for m:=1 to 3 do
   begin
   l:=Random(2);
   if l>0 then
   b[m]:=Random(10) // присваиваем положительные случайные целые числа от 0 до 9 элементам массива b если l>0
   else b[m]:=(-1)*Random(10); // присваиваем отрицательные случайные целые числа от 0 до 9 элементам массива b если l=0
   end;
   Memo1.Lines.Add('b1='+inttostr(b[1])+' b2='+inttostr(b[2])+' b3='+inttostr(b[3]));
  
   {Memo1.Lines.Add('a11='+inttostr(a[1,1])+' a12='+inttostr(a[1,2])+' a13='+inttostr(a[1,3])); //выводим коэффициенты первой строки;
   Memo1.Lines.Add('a21='+inttostr(a[2,1])+' a22='+inttostr(a[2,2])+' a23='+inttostr(a[2,3])); //выводим коэффициенты второй строки;
   Memo1.Lines.Add('a31='+inttostr(a[3,1])+' a32='+inttostr(a[3,2])+' a33='+inttostr(a[3,3])); //выводим коэффициенты третьей строки;
   }
   После успеха проверки можно вставить вычисление А1, А2, А3. Вывести их на Мемо(понадобится) и вывести решения системы в виде Х=А1/А и т. д.
   A1:= b[1]*(a[2,2]*a[3,3]-a[2,3]*a[3,2])-b[2]*(a[1,2]*a[3,3]-a[1,3]*a[3,2])+b[3]*(a[1,2]*a[2,3]-a[2,2]*a[1,3]); //вычисляем алг. дополнение А1
   A2:=a[1,1]*(b[2]*a[3,3]-b[3]*a[3,2])-a[2,1]*(b[1]*a[3,3]-a[1,3]*b[3])+a[3,1]*(b[1]*a[2,3]-b[2]*a[1,3]); //вычисляем алг. дополнение А2
   A3:= a[1,1]*(a[2,2]*b[3]-b[2]*a[3,2])-a[2,1]*(a[1,2]*b[3]-b[1]*a[3,2])+a[3,1]*(a[1,2]*b[2]-a[2,2]*b[1]); //вычисляем алг. дополнение А3
   по факту главная часть завершена.
   Если не брать во внимание, что вычисление обратной матрицы весьма трудоёмко, можно уже в таком виде выводить на печать. Но рано!
   Итого на этом этапе дополняем кодом чуть ниже вывода А1,А2,А3.
   Memo1.Lines.Add('A1='+inttostr(A1)+' A2='+inttostr(A2)+' A3='+inttostr(A3)); //выводим А1,А2,А3
   Memo1.Lines.Add('решение системы уравнений X='+inttostr(A1)+'/'+inttostr(A0)+ ' Y='+inttostr(A2)+'/'+inttostr(A0)+' Z='+inttostr(A3)+'/'+inttostr(A0)); //выводим решение системы уравнений
   По сути, "Метод Крамера" сделан. Годится для вывода ответов для преподавателя в его "шпаргалку" для проверки решений студентов.
   Далее уже идут сначала "косметические" дополнения, а после конкретно самое тяжёлое -- вычисление и вывод обратной матрицы. Она только на бумаге вручную легко считается. Закодировать формулу весьма тяжкая задача.
  -- Четвёртый этап: вывод нормального вида системы уравнений
   Собственно это составить не представляется особого труда, но есть одна тонкость.
   Состоит в том, что если просто ставить коэффициенты в строку вывода, то будут встречаться несуразности вида -2x+-5y+-3z=-2
   Вот это самое чередование плюс-минус весьма не эстетично.
   Для исправления этой неприятности на выводе в строку применю самый элементарный приём. Но для этого придётся ввести шесть строковых величин s[i,j].
   Так как отдельный вывод b1,b2,b3 не нужен, ставим на строке "заглушку" в виде //(комментарий). Ведь по сути этот вывод был "строительными лесами" и служил лишь для проверки, что всё нормально считается. Теперь же они и так будет в строке системы уравнений.
   Итого вывод на печать
   //цикл для определения содержания строковой s для заполнения системы уравнений
   for k:=1 to 3 do
   for m:=1 to 3 do
   begin
   if a[k,m]>=0 then s[k,m]:='+'
   else if a[k,m]<0 then s[k,m]:=''; // если коэффициент отрицательный, то выводиться должен чисто коэффициент со своим знаком и только
   end;
   // выводим систему уравнений
   Memo1.Lines.Add( IntToStr(a[1,1])+'x '+s[1,2]+IntToStr(a[1,2])+'y '+s[1,3]+IntToStr(a[1,3])+'z = '+ IntToStr(b[1]));
   Memo1.Lines.Add( IntToStr(a[2,1])+'x '+s[2,2]+IntToStr(a[2,2])+'y '+s[2,3]+IntToStr(a[2,3])+'z = '+ IntToStr(b[2]));
   Memo1.Lines.Add( IntToStr(a[3,1])+'x '+s[3,2]+IntToStr(a[3,2])+'y '+s[3,3]+IntToStr(a[3,3])+'z = '+ IntToStr(b[3]));
  
   A1:= b[1]*(a[2,2]*a[3,3]-a[2,3]*a[3,2])-b[2]*(a[1,2]*a[3,3]-a[1,3]*a[3,2])+b[3]*(a[1,2]*a[2,3]-a[2,2]*a[1,3]); //вычисляем алг. дополнение А1
   A2:=a[1,1]*(b[2]*a[3,3]-b[3]*a[3,2])-a[2,1]*(b[1]*a[3,3]-a[1,3]*b[3])+a[3,1]*(b[1]*a[2,3]-b[2]*a[1,3]); //вычисляем алг. дополнение А2
   A3:= a[1,1]*(a[2,2]*b[3]-b[2]*a[3,2])-a[2,1]*(a[1,2]*b[3]-b[1]*a[3,2])+a[3,1]*(a[1,2]*b[2]-a[2,2]*b[1]); //вычисляем алг. дополнение А3
  
   //вывод вычисленных значений А,А1,А2, А3 и решений системы уравнений
   Memo1.Lines.Add('A='+inttostr(A0)); //выводим значение определителя А;
   Memo1.Lines.Add('A1='+inttostr(A1)+' A2='+inttostr(A2)+' A3='+inttostr(A3)); //выводим А1,А2,А3
   Memo1.Lines.Add('решение системы уравнений X='+inttostr(A1)+'/'+inttostr(A0)+ ' Y='+inttostr(A2)+'/'+inttostr(A0)+' Z='+inttostr(A3)+'/'+inttostr(A0)); //выводим решение системы уравнений
  
  -- Шаг третий -- вывод ТРЁХ заданий на вариант
   Если внутри вариантов будет три задания, причём разные то, естественно понадобится повторное вычисление систем уравнений и их решений. Однако, как указывалось ранее, особо злючий момент -- вычисление обратной матрицы". А это третье задание.
   Отсюда и алгоритм:
   внутри цикла, что охватывает генерацию "Вариант N", вводится ещё один цикл, который отвечает за номера заданий. Пусть эти номера у нас будут обозначаться переменной с именем NN. Таким образом мы можем вставить отдельно блок решения и вывода на Мемо обратной матрицы в третьем задании.
   Поэтому:
  -- Первый этап: разделение вариантов на три задания
   Выглядеть это будет так:
   for N:=1 to SpinEdit1.Value do
   begin
   Memo1.Lines.Add('Вариант '+inttostr(N)); //выводит "вариант N"
   for NN:=1 to 3 do
   begin
   Memo1.Lines.Add('Задание '+inttostr(NN));
   if NN=1 then Memo1.Lines.Add('Решите систему уравнений методом Гаусса')
   else if NN=2 then Memo1.Lines.Add('Решите систему уравнений методом Крамера')
   else Memo1.Lines.Add('Решите систему уравнений методом обратной матрицы');
  
   Далее, то что уже написанный код, но в хвосте решений следующий блок:
   if NN=3 then
   begin
   Memo1.Lines.Add('Обратная матрица третьего задания');
   //(далее формула вычисления и выведение обратной матрицы в Мемо)
   end; // закрыт if с обратной матрицей
   end; // закрыт цикл NN
   end; //закрыт цикл для N
   Последние приписки для end; это для описания. В Дельфи связки begin-end подсвечиваются программно и видно что где открывается-закрывается. Однако вот такая форма для разработки блоков удобна: где-то накатал с "комментами", а после вставил в Дельфи. Позатирать лишние строки комментов -- пара секунд.
  
  -- Второй этап: разработка кода на вычисление и вывод обратной матрицы
   Здесь предоставляю читателям самим вывести формулу обратной матрицы (для тренировки) по алгоритмам и формулам, приведённым здесь: https://ru.wikipedia.org/wiki/Обратная_матрица
   см. параграф "С помощью матрицы алгебраических дополнений".
   Вставка блока будет делаться так:
   if NN=3 then
   begin
   Memo1.Lines.Add('Обратная матрица третьего задания');
   //(далее формула вычисления элементов обратной матрицы)
   c[1,1]:=
   ..........
   c[3,3]:=
   Memo1.Lines.Add(inttostr(c[1,1])+'/'+inttostr(A0)+' '+inttostr(c[1,2])+'/'+inttostr(A0)+' '+inttostr(c[1,3])+'/'+inttostr(A0));
   Memo1.Lines.Add(inttostr(c[2,1])+'/'+inttostr(A0)+' '+inttostr(c[2,2])+'/'+inttostr(A0)+' '+inttostr(c[2,3])+'/'+inttostr(A0));
   Memo1.Lines.Add(inttostr(c[3,1])+'/'+inttostr(A0)+' '+inttostr(c[3,2])+'/'+inttostr(A0)+' '+inttostr(c[3,3])+'/'+inttostr(A0));
   end; // закрыт if с обратной матрицей
   end; // закрыт цикл NN
   ------
   После этого вставляем пару строк для того, чтобы отделить очередной вариант от следующих.
   Memo1.Lines.Add(' ');
   Memo1.Lines.Add(' ');
   //Дальше я вставил блок страховки от ошибок. Просто обнуляются переменные перед их повторным использованием
   for k:=1 to 3 do
   begin
   for m:=1 to 3 do
   begin
   a[k,m]:=0;
   A0:=0;
   c[k,m]:=0;
   end;
   end; // закрыт блок N
   end; // конец процедуры обработки нажатия клавиши "Вычислить"
   Дело в том, что иногда "на ровном месте" уже полученный исполняемый файл выдаёт сообщение об ошибке и виснет, несмотря на ранее устойчивую и правильную работу. Принудительное обнуление как раз и позволяет программе нормально работать без сбоев. Не знаю как у вас, но у меня вышло так.
  -- Шаг третий: контроль работы и отсечение несуразностей
   Просто так код может и работает. Компилятор слопает всё, что не противоречит правилам его работы и выдаст вам исполняемый файл.
   Но вот правильно ли всё в формулах написано, надо проверять.
   При запуске программы берём любой вариант и выборочно пересчитываем вручную то, что надо вычислять. А именно: А0, А1,А2,А3.
   При тестовом прогоне и пересчёте варианта вылезло то, что в формуле вычисления А2 была допущена мелкая ошибка в индексах. Исправлено.
   При следующем прогоне уже всё считалось правильно.
  
   Ещё одна несуразность -- возможность выставления в индексы для x,y,z нулей. Если их на систему уравнений один-два, это ещё терпимо. Но есть ненулевая вероятность появления нулей у половины и более индексов при НЕНУЛЕВОМ значении определителя. Что весьма нехорошо с т.з. проверки умений учащихся. Т.к. натуральная холява.
   Последнее "лечится" легко.
   Просто вокруг блока, где назначаются значения конкретным индексам ставим цикл с постусловием что число не должно быть равно нулю.
   Вот так:
   repeat
  
   l:=Random(2);
   if l>0 then
   a[k,m]:=Random(4) // присваиваем положительные случайные целые числа от 0 до 9 элементам массива если l>0
   else a[k,m]:=(-1)*Random(4); // присваиваем отрицательные случайные целые числа от 0 до 9 элементам массива если l=0
   until(a[k,m]<>0);
   _________________________
  
   Можно и ещё "блох" поотлавливать. Но и этого уже достаточно.
  
  
  
  
   Post Scriptum:
   Кстати для студентов!
   Скопипастить код с решениями системы методом Крамера, с вычислением обратной матрицы(если вы его таки сделали ;-) ), а после переделать полученный код под решение КОНКРЕТНЫХ систем с КОНКРЕТНЫМИ коэффициентами -- элементарно!
   Delphi вам в лапы и ветер в спину! :-))

 Ваша оценка:

Связаться с программистом сайта.

Новые книги авторов СИ, вышедшие из печати:
О.Болдырева "Крадуш. Чужие души" М.Николаев "Вторжение на Землю"

Как попасть в этoт список
Сайт - "Художники" .. || .. Доска об'явлений "Книги"