- Рассмотрим задачу решения нелинейного уравнения с одним неизвестным. Такие уравнения можно решать в MS Excel (Меню | Сервис | Подбор параметра), но будем использовать Делфи.
С чего начинается создание программы (проекта)?
Прежде всего, программист должен знать, какие действия нужно выполнить в ходе решения.
На этом этапе язык программирования не нужен. Достаточно русского языка и математического.
- Например, в этой задаче нужно знать - что такое корень. Для меня корень уравнения
f(x)= 0 - это такое значение аргумента x функции f(x), при котором значение функции равно нулю.
- Второе, что нужно знать - какие методы (математические - так как задача эта - математическая) существуют для решения. Есть такие уравнения (например, квадратные) для решения которых существует формула. Но в общем случае формул нет, а есть методы (т е алгоритмы). Здесь мы применим метод дихотомии.
- Он основан на таком факте: если непрерывная функция f(x) имеет разные знаки (т е + - ) на краях некоторого интервала (a..b), то имеется нечетное (в частности 1) количество корней уравнения f(x) = 0 на этом интервале. Разумеется, функция должна быть хорошего поведения (не иметь разрывов). Вы можете в этом убедиться на рис.1
- Во-первых, найдем интевал a..b для которого f(a)*f(b) < 0, т е знаки разные.
(Для этого может пригодиться
проект Калькулятор, который может рисовать графики функций и находить корни). В нашем проекте предусмотрим вычисление f(x) при заданном x. Тогда интервал a..b несложно найти подбором.
- Далее применим метод дихотомии. Заключается он в следующем:
- Делим интервал a..b пополам. Получаем c=(a+b)/2; Вычисляем f(c);
Знак f(c) совпадает либо со знаком f(a) либо со знаком f(b) (либо f(c)=0 и с является корнем);
- Если знак f(c) совпадает со знаком f(a), то корень находится на интервале c..b.
Видим, что ситуация повторяется, поэтому делим пополам интервал c..b, то есть повторяем
пункт a.
- В противном случае, то есть если знак f(c) совпадает со знаком f(b), то корень находится на интервале a..c. Тогда делим пополам интервал a..b, то есть повторяем пункт a.
- В результате интервал, в котором находится корень, уменьшается на каждом шаге в 2 раза, и это повторяется до тех пор, пока интервал не станет меньше заданной допустимой погрешности для значения корня.
- Описанные действия можно представить на языке Паскаль в виде процедуры:
PROCEDURE DIX(a, {начало интервала}
b, {конец интервала}
Eps, {допустимая погрешность значения корня}
Eps1:extended; {значение функции, принимаемое за нуль}
var x, {найденное значение корня}
r:extended; {значение функции f(x)}
fu: {подпрограмма вычисления функции} func1);
var i,nn:integer; { Метод дихотомии. }
BEGIN nn:=0; i:= sign(fu(a));
while b - a > Eps do
begin
x := 0.5*(a + b); r := fu (x);
If abs(r) < Eps1 then Exit;
If Sign(r) = i then a:= x else b:= x;
{Здесь Sign()- функция, определяющая знак + - аргумента}
end;
END;
Как видите, здесь нет ничего нового в сравнении с Турбо-Паскалем7.
Если Вы забыли о подпрограммах, посмотрите лекцию 5.
О процедурных типах Вы можете прочесть в лекции 8.
- Теперь нужно выдумать вспомогательную функцию, определяющую знак + -,
потому что применять критерий f(a)*f(b) < 0 рискованно (результат умножения может выйти за диапазон допустимых значений). Вот эта функция, надеюсь, понятная:
FUNCTION SIGN(x:extended):integer;
{ Определяет знак + - выражения x. Если x<0 - возвращает -1,
если x>0 - возвращает +1, если x=0 - возвращает 0 }
BEGIN
If x < 0 then begin sign := -1; exit;
end;
if x > 0 then begin sign := 1; exit;
end;
sign := 0;
END;
- В качестве примера решим уравнение 10*x^3 - 25*x^2 + 15*x - 1 = 0;
Чтобы решить уравнение, понадобится функция, вычисляющая значение левой части уравнения.
Вот эта функция:
function ff(x:extended):extended;
// Замените начинку, если нужна другая функция
begin
ff:= 10*x*x*x - 25*x*x + 15*x -1;
end;
Именно эта функция вызывается из процедуры DIX.
- Почти все готово для решения задачи. Но как найти значения a и b? В этой программе
используется построение графика функции ff на интервале (a..b), причем пользователь
может ввести значения a и b в окна ввода и затем щелкнуть по кнопке "Построить график".
- Здесь сделаем небольшую остановку и обсудим отличия ввода/вывода данных в Делфи в сравнении
с Турбо-Паскалем. Хотя в Делфи для ввода-вывода можно использовать процедуры read и write
(см пример консольного приложения Делфи), все же обычно используется графический интерфейс, так как это удобнее.
С этой целью применяют что-то вроде полуфабрикатов, а именно, компоненты Делфи. Для использования в проектах компоненты обычно нужно настроить, т е задать значения свойствам и обработчики для событий.
К нашему счастью, свойства компонентов имеют значения по умолчанию, поэтому настраивать приходится немного.
- Рассмотрим наши действия по порядку:
- Запускаем среду Делфи7 (т е файл .\Program Files\Borland\Delphi7\Bin\delphi32.exe)
- Появляется несколько окон. Основные из них:
Главное окно. Здесь имеется меню, панели с кнопками, панель компонентов (несколько закладок).
Именно с панели компонентов мы перетаскиваем компоненты на форму. При запуске Делфи появляется
пустая форма. Из пунктов меню часто используется File - для создания нового проекта (можно выбать тип проекта, в том числе - консольный), для открытия существующего проекта (при продолжении работы над проектом),
для сохранения проекта.
Это окно формы. Его размеры можно менять мышкой. Свойства формы, которые часто настраивают:
- Caption (заголовок). По умолчанию - Form1. Можно (при помощи инспектора объектов - о нем дальше) заменить заголовок на "Это мой первый проект" или "Решение уравнений методом дихотомии" и т п.
- Color т е цвет,
- BorderStyle - Можно задать форму постоянных размеров (bsSingle)
- Font.CharSet = RUSSIAN_CHARSET Отметим, что Font (шрифт), являющийся свойством формы, является объектом и мы здесь настроили свойство CharSet объекта Font. Часто определяют еще размер (Size) этого же объекта Font. Заметим, что настроенные свойства Font НАСЛЕДУЮТСЯ установленными на форму объектами.
- Position иногда полезно, чтобы форма располагалась по центру экрана при запуске приложения. Для этого выберите Position = poScreenCenter из выпадающего списка возможных значений этого свойства.
- TransparentColor, TransparentColorValue используйте их, если нужно часть формы сделать прозрачной, например, сделать круглую форму. Но при этом имейте в виду, что старые оп. системы (Windows 95,98
и т п) не поддерживают прозрачность.
Это окно инспектора объектов. С его помощью настраиваются свойства формы и компонентов, установленных на форму. Вы можете выделить любой компонент на форме (с помощью мышиного щелчка), и свойства выделенного объекта будут видны и доступны для изменения в инспекторе объектов. В частности, на этой картинке инспектор объектов отображает свойства формы Form1. Видно, как программист установил свойство Position. Отметим также, что инспектор объектов имеет 2 закладки: Свойства (Properties) и События (Events). События - что-то новое для нас, поговорим о них немного.
Примерами событий могут служить: щелчок мышкой, сбой при чтении файла или выполнении действий над данными, изменение позиции и размера окон и т п. События обнаруживаются операционной системой, которая, в свою очередь, информацию о событии посылает приложению. Компоненты Делфи способны реагировать на некоторые из событий. Список таких событий Вы увидите раскрыв закладку Events. Пока что мы будем пользоваться только событием onClick (мышиный щелчок по компоненту - кнопке). Говоря коротко, если сделать двойной щелчок в инспекторе - правее имени события, то в редакторе текста появится заготовка процедуры, в которой мы должны записать действия программы при наступлении этого события.
Но об этом заботиться рано. Займемся пока текстом проекта.
Делфи-проект состоит из модулей, и когда при запуске Делфи создается проект, то в окне редактора появляется заготовка текста модуля. Что интересного можно здесь увидеть?
- Текст начинается с заголовка модуля unit Unit1; Видим что имя модуля (как и многие другие имена) создается автоматически (но мы можем переименовать).
interface - объявляется начало интерфесной части модуля, (т е все, что здесь описано, можно сделать доступным в других модулях проекта.)
Далее записано предложение uses:
uses
Windows, Messages, ...
Здесь перечисляются модули, используемые в данном модуле. Не будем пока это обсуждать.
Затем идут описания типов, в которых уже вписано описание КЛАССА TForm1 - это класс, описывающий создаваемую нами форму. Он будет наполняться при добавлении компонентов на форму. Думаю, что Вам полезно вспомнить, что такое класс.
Далее следуют описания переменных, имеется всего 1 переменная: Form1: TForm; Это переменная - объект. Можно сказать, что типом для объекта является класс, так как описание деталек объекта содержит именно класс.
- Начнем добавление компонентов на форму. Посмотрите готовый проект. Как Вы видите, в левом верхнем углу формы находится текст:
1. Если функция...
Для этого перетащим компонент Label, установим его свойства: AutoSize = false, WordWrap = true, впишем текст в Caption и мышкой установим нужные размеры текста. Так как при настройке свойств формы было установлено: Font.Size = 10, то шрифт у Label1 тоже имеет Size = 10.
- Добавим рамку с заголовком "Задайте интервал a..b ..." (компонент GroupBox1), зададим ей размеры и текст.
- Далее разместим внутри GroupBox1 компоненты Label2..5 для надписей a= b= f(a)= f(b)= а также
Label6,7 - для показа значений f(a) f(b). В свойство Caption можно вписать 0.
- Нужно также поставить 2 компонента (класс TEdit) для ввода знаений a и b. Изменим имена этих редакторов на edA и edB (Это не обязательно, но удобно). Впишем свойство edA.Text = -0,2 и
edB.Text = 2
- Теперь можно поставить кнопку (Button) с надписью "Построить график...". Притащите ее с панели компонентов, впишите надпись в свойство Caption. Чтобы не путаться, присвоим кнопке имя (свойство name) btnCalc. Создадим также процедуру - обработчик щелчка по этой кнопке. Для этого: выделим кнопку, в инспекторе объектов перейдем на вкладку Events, найдем событие OnClick и сделаем двойной щелчок в правом окошке строки OnClick.
- Тогда в окне редактора появится шаблон-заготовка процедуры:
procedure TForm1.btnCalcClick(Sender: TObject);
begin
end;
Очень важно: если нужно удалить процедуру обработчика события, удалите из нее все, что Вы вписали,
НО ОСТАВЬТЕ первоначальный шаблон. После этого просто сохраните проект и обработчики с пустым телом
(т е пусто между begin и end) будут удалены автоматически. Если же Вы вручную удалите весь текст процедуры, то возникнет ошибка.
Для начала впишем сюда действия по вычислению f(a) и f(b):
aa:= strToFloat(edA.Text);// для хранения a,b использую переменные aa, bb
bb:= strToFloat(edB.Text);
{ функция strToFloat(<строка>) - преобразует текстовую запись числа в вещественное число }
{Так как недопустимо, чтобы a = b, запишем:}
if aa = bb then
begin
showmessage('b = a - недопустимо'); //выводит этот текст в окошке
exit; // выход из подпрограммы
end;
str(ff(aa):0:4,sss); // имеется в виду, что функция ff уже вписана в текст модуля.
{полный текст модуля на этой стадии отлаживания см ниже. Процедура str (она есть
и в Турбо-Паскале) преобразует число в его строковое представление.
При этом выполняется форматирование - в дробной части показывать 4 цифры }
Label6.Caption:=' '+ sss; // Полученная строка присваивается свойству Label6.Caption
{в результате число показано на форме}
str(ff(bb):0:4,sss);
Label7.Caption:=' '+ sss;
{----------- Полный текст модуля main.pas ----------------------}
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
func1 = function(x:extended): extended;
TForm1 = class(TForm)
Label1: TLabel;
GroupBox1: TGroupBox;
Label2: TLabel;
Label3: TLabel;
edA: TEdit;
edB: TEdit;
Label4: TLabel;
Label5: TLabel;
btnCalc: TButton;
Label6: TLabel;
Label7: TLabel;
procedure btnCalcClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var aa,bb: extended;
function ff(x:extended):extended;
// Замените начинку, если нужна другая функция
begin
ff:= 10*x*x*x - 25*x*x + 15*x -1;
end;
procedure TForm1.btnCalcClick(Sender: TObject);
var sss: string; axkoef, bxkoef, aykoef, bykoef,dx,
f1,fmin,fmax: double; Reka : TRect;
fun: array [0..100] of double; ii:integer;
begin
aa:= strToFloat(edA.Text); // ГРАНИЦЫ х
bb:= strToFloat(edB.Text);
if aa = bb then
begin
showmessage('b = a - недопустимо');
exit;
end;
str(ff(aa):0:4,sss);
Label6.Caption:=' '+ sss;
str(ff(bb):0:4,sss);
Label7.Caption:=' '+ sss;
end;
end.
Этот модуль может только вычислять функцию в двух заданных точках. В дальнейшем мы добавим все остальное. Но этот модуль работает и можно видеть результаты. Теперь нужно сохранить проект. Советую каждый проект сохранять в отдельной папке. Щелкните кнопку "Save All" или выберите в меню: File | Save All. Откроется окно, в котором выберите или создайте папку для проекта. Обратите внимание, что требуется 2 сохранения: для модуля и для проекта. При этом модуль и проект можно переименовать. После сохранения полезно заглянуть в эту папку и убедиться, что там записаны все файлы проекта (по умолчанию это Unit1.pas, Unit1.dfm, Project1.cfg,
Project1.dof, Project1.dpr, Project1.res). Если эти файлы сохранены, то для продолжения работы с проектом необходимо и достаточно открыть Project1.dpr. Основное внимание мы уделяли файлу Unit1.pas - в нем содержится текст модуля Unit1. В тексте содержится описание класса TForm1, подпрограмма ff. Часть информации сохраняется в файле Unit1.dfm и именно поэтому нельзя удалять обработчики событий простым редактированием файла Unit1.pas - так как получается противоречие между Unit1.pas и Unit1.dfm. Остальные файлы проекта не нуждаются во внимании программиста, однако при переносе на другой компьютер могут "потеряться" файлы. В этом случае загляните в текст
файла Project1.dpr и обратите внимание на предложение вроде:
Unit1 in 'Unit1.pas' {Form1};
Здесь после in записывается путь к файлу Unit1.pas. Если все файлы проекта хранятся в одной папке, то путь к файлу пустой - указывается только имя файла. Если же Вы сохраняли проект не совсем правильно, то может записаться путь вроде: Unit1 in 'D:\temp\Unit1.pas' {Form1};
а на другой машине такой папки нет. Тогда
подредактируйте Project1.dpr и укажите правильный путь. Лучше всего поместить все файлы проекта в одну папку и убрать пути.
Проект можно компилировать (нажать Ctrl-F9). При этом могут появиться сообщения об ошибках.
После устранения ошибок компиляция завершится нормально и в папке появится исполняемый файл Project1.exe. Этот файл можно запускать (открывать) на любом другом компьютере, если он работает под Windows (т е среда Делфи для этого не нужна). Исполняемые файлы не совсем простых программ могут не запускаться на другом компьютере, так как при компановке не подключены некоторые библиотеки. Эта проблема решается в Меню | Project | Options | Packages | Runtime Packages
Также при компиляции не совсем простых программ могут "не найтись" некоторые библиотеки, не входящие в комплект Делфи. Такие библиотеки подключаются в Меню | Project | Options | Directories | Search Path - здесь добавляются в проект пути к дополнительным библиотекам.
Можно совместить компиляцию с запуском на выполнение - нажмите клавишу F9.
Итак, проект запущен и работает.
Теперь можно подбором найти такие близкие значения a и b, чтобы f(a) и f(b) имели разные знаки + - и затем применить метод дихотомии на этом интервале a..b. Именно этим мы сейчас и займемся, а построение графика сделаем позже.
- Итак, простенький работающий проект у нас есть. Будем его наращивать. Добавим в модуль тексты подпрограмм Sign, Dix (обсуждались выше). Поставим на форму Label8 для многострочного текста:
3. Программа уточнит... затем Label9 и 20 для текстов: Задайте EPS Задайте EPS2,
окошки edEPS edEPS2 для ввода параметров eps, eps1 процедуры DIX. Поставим также компоненты
Label10,11,18,19 - для надписей "Корень х = " "f(x) = " и значений: функции и корня. Можно поставить компонент GroupBox2 с надписью "Результат" или обойтись без него.
- Поставим еще кнопку btnDixot с надписью "Уточнить значение корня" и создадам процедуру обработки щелчка по этой кнопке (его Вы увидите ниже - в дополненном тексте модуля - см ниже)
Здесь Вы видите результат решения этого ур-я методом дихотомии, а ниже - текст модуля.
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
func1 = function(x:extended): extended;
TForm1 = class(TForm)
Label1: TLabel;
GroupBox1: TGroupBox;
Label2: TLabel;
Label3: TLabel;
edA: TEdit;
edB: TEdit;
Label4: TLabel;
Label5: TLabel;
btnCalc: TButton;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
Label9: TLabel;
edEPS: TEdit;
btnDixot: TButton;
GroupBox2: TGroupBox;
Label18: TLabel;
Label19: TLabel;
Label20: TLabel;
edEPS2: TEdit;
Label10: TLabel;
Label11: TLabel;
procedure btnCalcClick(Sender: TObject);
procedure btnDixotClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
PROCEDURE DIX(a,b,Eps,Eps1:extended; var x,r:extended; fu:func1);
FUNCTION SIGN(x:extended):integer;
var
Form1: TForm1;
implementation
{$R *.dfm}
var aa,bb,fa,fb,eps,eps2,xx,rr: extended;
xmin, xmax, ymin, ymax: Integer;
function ff(x:extended):extended;
// Замените начинку, если нужна другая функция
begin
ff:= 10*x*x*x - 25*x*x + 15*x -1;
end;
FUNCTION SIGN(x:extended):integer; { Определяет знак + - выражения x }
BEGIN
If x < 0 then begin sign := -1; exit;
end;
if x > 0 then begin sign := 1; exit;
end;
sign := 0;
END;
PROCEDURE DIX(a,b,Eps,Eps1:extended; var x,r:extended; fu:func1);
var i,nn:integer; { Метод дихотомии. Циклически делит интервал a..b
пополам и определяет, на какой из двух половин находится корень}
BEGIN nn:=0; i:= sign(fu(a));
while b - a > Eps do
begin
x := 0.5*(a + b); r := fu (x);
If abs(r) < Eps1 then Exit;
If Sign(r) = i then a:= x else b:= x;
end;
END;
procedure TForm1.btnCalcClick(Sender: TObject);
{строит график функции ff(x) в заданнном интервале a..b
для этого предварительно находит мин и макс ff, определяет
коэффициенты линейного отображения a*x + b графика на заданную часть формы.
(это коэффициенты axkoef, bxkoef, aykoef, bykoef - по X и Y
соответственно)}
var sss: string;
begin
aa:= strToFloat(edA.Text); // ГРАНИЦЫ х
bb:= strToFloat(edB.Text);
if aa = bb then
begin
showmessage('b = a - недопустимо');
exit;
end;
str(ff(aa):0:4,sss);
Label6.Caption:=' '+ sss;
str(ff(bb):0:4,sss);
Label7.Caption:=' '+ sss;
end;
procedure TForm1.btnDixotClick(Sender: TObject);
var ss: string;
begin // запускаю процедуру дихотомии
eps:= StrToFloat(edEPS.Text);
eps2:= StrToFloat(edEPS2.Text);
DIX(aa,bb,Eps,Eps2,xx,rr,ff);
str(xx:0:4,ss);
Label19.Caption:= ss;
str(rr:0:4,ss);
Label11.Caption:= ss;
end;
end.
- Добавим наконец, построение графика функции на интервале a..b. Что нужно сделать?
- Разобьем (a..b) на 100 равных интервалов, вычислим и сохраним (в массиве) значения функции на
границах этих интервалов.
- На форме зададим прямоугольный участок для графика. Пусть его верхняя граница отстоит на 20 пикселей
от верхнего края формы (ymin := 20;), левая граница - отстоит на 20 пикс. от правого края надписи Label1
(xmin:= Label1.Left + Label1.Width + 20; где Width - ширина надписи), правая граница отстоит
на 20 пикс от правого края формы (xmax:= form1.ClientWidth -20; здесь ClientWidth - ширина клиентской
(свободной) части формы), нижняя граница - на 20 пикс. выше верхнего края надписи Label12, (т е
ymax:= Label12.Top - 20;)
Форма обладает свойством Canvas (канва). На поверхности этой канвы можно рисовать, используя декартовые
координаты X,Y, причем начало координат находится в верхнем левом углу (в данном случае - формы),
ось Х смотрит вправо, ось Y - вниз. Выше мы нашли координаты углов прямоугольной границы графика:
xmin, xmax, ymin, ymax именно в этой системе координат.
- Теперь наша задача - найти коэффициенты линейного отображения переменных x,y=f(x) для которых
строится график - в координаты экрана. Рассмотрим более простую задачу - для координаты Х.
Искомое преобразование имеет вид Хэкр = axkoef*x + bxkoef. Тогда для определения axkoef и bxkoef
имеем систему из двух уравнений:
xmin = axkoef*aa + bxkoef;
xmax = axkoef*bb + bxkoef;
Аналогичная задача решается и для оси Y, но требуется предварительно найти мин и макс значения
функции. Упрощая, нахожу макс и мин для сохраненного массива значений функции - fmax и fmin,
используя типовой алгоритм (см далее текст модуля). Нужно еще учесть, что fmax должен отображаться вверху,
т е в экранную координату ymin. Тогда имеем систему уравнений:
ymin = aykoef*fmax + bykoef;
ymax = aykoef*fmin + bykoef;
В тексте модуля приводятся готовые решения для этих коэффициентов.
- Начинаем рисовать график. Ставим перо в начальную точку с координатами:
Round(axkoef*aa+bxkoef), Round(aykoef*fun[0]+bykoef), где fun[0] - значение функции при х = аа.
Функция Round необходима, так как экранные координаты - целые числа.
Это выполняет МЕТОД MoveTo свойства-объекта Canvas формы Form1.
Затем (в цикле) проводится линия до следующей точки графика при помощи метода LineTo:
LineTo(Round(axkoef*(aa+dx*ii)+bxkoef), Round(aykoef*fun[ii]+bykoef));
Здесь - ii - порядковый номер точки (начальная - 0), dx - шаг по х, т е длина интервала разбиения
оси Х.
- Рисуем ось Х, для этого Y = 0. То есть - выполняем процедуры:
moveto(Round(axkoef*aa+bxkoef), Round(bykoef));
lineto(Round(axkoef*bb+bxkoef), Round(bykoef));
- Рисуем засечки на оси Х через единицу.
- Кроме этого, необходимо очищать прямоугольную зону графика перед каждой прорисовкой.
С этой целью используем метод Canvas.FillRect(), который рисует заполненный прямоугольник.
Параметром у этого метода является прямоугольник и мы должны его создать и определить.
Среди описаний локальных переменных процедуры btnCalcClick добавим Reka : TRect; (TRect - тип, описывающий
прямоугольники.)
type
TRect = packed record
case Integer of
0: (Left, Top, Right, Bottom: Integer);
1: (TopLeft, BottomRight: TPoint);
end;
Зададим координаты этого прямоугольника:
Reka.Left := xmin-1;
Reka.Top := ymin-6;
Reka.Right := xmax+1;
Reka.Bottom := Ymax+6;
а также цвет и стиль заполнения:
Canvas.Brush.Color := RGB($BB,$DD,$DD);
Canvas.Brush.Style := bssolid;
Canvas.FillRect(Reka); // чищу место для графика
Свойство-объект Brush(кисть) описывает заливку графического
объекта. Style = bssolid означает сплошную заливку.
Функция RGB преобразует компоненты цвета (Red, Green, Blue) в цвет.
Компоненты цвета записаны в 16-чной кодировке.
- Однако некрасиво, что в запущенном проекте зона графика остается пустой до
щелчка по кнопке "Построить график". Чтобы этого не было, поставил на форму компонент
Timer1, который каждые Timer1.Interval миллисекунд генерирует событие onTimer.
Задал Timer1.Interval = 40 (т е 0,04 сек), а в обработчик события onTimer вписал:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled:= false;
btnCalcClick(Sender);
end;
То есть таймер прежде всего отключает сам себя (Enabled - доступный),
затем "нажимает кнопку btnCalc", т е запускает процедуру btnCalcClick
- Полный текст законченного модуля:
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
func1 = function(x:extended): extended;
TForm1 = class(TForm)
Label1: TLabel;
GroupBox1: TGroupBox;
Label2: TLabel;
Label3: TLabel;
edA: TEdit;
edB: TEdit;
Label4: TLabel;
Label5: TLabel;
btnCalc: TButton;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
Label9: TLabel;
edEPS: TEdit;
btnDixot: TButton;
GroupBox2: TGroupBox;
Label18: TLabel;
Label19: TLabel;
Label20: TLabel;
edEPS2: TEdit;
Label10: TLabel;
Label11: TLabel;
Timer1: TTimer;
Label12: TLabel;
procedure btnCalcClick(Sender: TObject);
procedure btnDixotClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
PROCEDURE DIX(a,b,Eps,Eps1:extended; var x,r:extended; fu:func1);
FUNCTION SIGN(x:extended):integer;
var
Form1: TForm1;
implementation
{$R *.dfm}
var aa,bb,fa,fb,eps,eps2,xx,rr: extended;
xmin, xmax, ymin, ymax: Integer;
function ff(x:extended):extended;
// Замените начинку, если нужна другая функция
begin
ff:= 10*x*x*x - 25*x*x + 15*x -1;
end;
FUNCTION SIGN(x:extended):integer; { Определяет знак + - выражения x }
BEGIN
If x < 0 then begin sign := -1; exit;
end;
if x > 0 then begin sign := 1; exit;
end;
sign := 0;
END;
PROCEDURE DIX(a,b,Eps,Eps1:extended; var x,r:extended; fu:func1);
var i,nn:integer; { Метод дихотомии. Циклически делит интервал a..b
пополам и определяет, на какой из двух полоин находится корень}
BEGIN nn:=0; i:= sign(fu(a));
while b - a > Eps do
begin
x := 0.5*(a + b); r := fu (x);
If abs(r) < Eps1 then Exit;
If Sign(r) = i then a:= x else b:= x;
end;
END;
procedure TForm1.btnCalcClick(Sender: TObject);
{строит график функции ff(x) в заданнном интервале a..b
для этого предварительно находит мин и макс ff, определяет
коэффициенты линейного отображения a*x + b графика на заданную часть формы.
(это коэффициенты axkoef, bxkoef, aykoef, bykoef - по X и Y
соответственно)}
var sss: string; axkoef, bxkoef, aykoef, bykoef,dx,
f1,fmin,fmax: double; Reka : TRect;
fun: array [0..100] of double; ii:integer;
begin
xmin:= Label1.Left + Label1.Width + 20;
xmax:= form1.ClientWidth -20;
ymax:= Label12.Top - 20;
ymin := 20;
Reka.Left := xmin-1;
Reka.Top := ymin-6;
Reka.Right := xmax+1;
Reka.Bottom := Ymax+6;
Canvas.Brush.Color := RGB($BB,$DD,$DD);
Canvas.Brush.Style := bssolid;
Canvas.FillRect(Reka); // чищу место для графика
aa:= strToFloat(edA.Text); // ГРАНИЦЫ х
bb:= strToFloat(edB.Text);
if aa = bb then
begin
showmessage('b = a - недопустимо');
exit;
end;
dx:= (bb-aa)/100; // шаг по Х (101 точка на графике)
axkoef:= (xmax - xmin)/(bb-aa);
bxkoef := xmin - axkoef*aa;
{коэфф определ из условия, ЧТОБЫ края графика попали на края
отведенного для него прямоугольника}
fun[0] := ff(aa); fmin:= fun[0];
fmax:= fun[0];
for ii:= 1 to 100 do {запоминаю значения ф-ии, нахожу мин и макс}
begin fun[ii]:= ff(aa+dx*ii);
if fun[ii] > fmax then fmax:= fun[ii];
if fun[ii] < fmin then fmin:= fun[ii];
end;
fa:= fun[0];
fb:= fun[100];
aykoef:= (ymin - ymax)/(fmax-fmin);
bykoef := ymax - aykoef*fmin;
with Canvas do
begin
moveto(Round(axkoef*aa+bxkoef), Round(aykoef*fa+bykoef));
for ii:=1 to 100 do
lineto(Round(axkoef*(aa+dx*ii)+bxkoef), Round(aykoef*fun[ii]+bykoef));
// график строю из прямых отрезков
moveto(Round(axkoef*aa+bxkoef), Round(bykoef));
lineto(Round(axkoef*bb+bxkoef), Round(bykoef)); // черчу ось X
for ii:= -20 to 20 do
if (ii>=aa) and (ii<=bb) then
begin // засечки через 1 на оси Х
moveto(Round(axkoef*ii+bxkoef), Round(bykoef - 5 ));
lineto(Round(axkoef*ii+bxkoef), Round(bykoef + 5 ));
end;
end;
str(fa:0:4,sss);
Label6.Caption:=' '+ sss;
str(fb:0:4,sss);
Label7.Caption:=' '+ sss;
end;
procedure TForm1.btnDixotClick(Sender: TObject);
var ss: string;
begin // запускаю процедуру дихотомии
eps:= StrToFloat(edEPS.Text);
eps2:= StrToFloat(edEPS2.Text);
DIX(aa,bb,Eps,Eps2,xx,rr,ff);
str(xx:0:4,ss);
Label19.Caption:= ss;
str(rr:0:4,ss);
Label11.Caption:= ss;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled:= false;
btnCalcClick(Sender);
end;
end.
- Вот вроде и все ! Пишите, спрашивайте.