Главная arrow В помощь студентам arrow Статьи по программированию в Delphi arrow Некоторые нюансы вывода графиков функций  
23.11.2024 г.
Некоторые нюансы вывода графиков функций Печать E-mail
Автор Administrator   
11.03.2009 г.

Автор: Алексей Легкунец, Королевство Delphi

Изучая доступную литературу по программированию, которую я нашел в Интернете, а также некоторые программы, я пришел к выводу, что программисты то ли не осознают, то ли не хотят напрягаться на эту тему, и всё делают, как в школе учили. Строят графики, как на бумаге. Тем самым умаляя возможности компьютера. Оставляя те же недостатки метода построения, и даже усугубляя их.

Во первых вывод на экран - это вывод на дискретный носитель. Этот факт почти никак не учитывается. В тексте будет пояснено.

А сейчас я приведу пример программы из одного учебного электронного издания, автора я привести не могу, т.к. последний не указан.

unit Graf;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;
type
  TForm1 = class(TForm)
    procedure FormPaint(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 Function f(x:real):real;
begin
f:=2*Sin(x)*exp(x/5);
end;
// строит график функции
procedure GrOfFunc;
var
x1,x2:real; // границы изменения аргумента функции
y1,y2:real; // границы изменения значения функции
x:real; // аргумент функции
y:real; // значение функции в точке х
dx:real; // приращение аргумента
 
 
l,b:integer; // левый нижний угол области вывода графика
w,h:integer; // ширина и высота области вывода графика
mx,my:real; // масштаб по осям X и Y 
x0,y0:integer; // точка - начало координат
begin                                           // область вывода графика
l:=10;                                          // X - координата левого верхнего угла
b:=Form1.ClientHeight-20;               //У - координата левого верхнего угла
h:=Form1.ClientHeight-40; // высота
w:=Form1.Width-40; // ширина
x1:=0; // нижняя граница диапазона аргумента
x2:=25; // верхняя граница диапазона аргумента
dx:=0.01; // шаг аргумента
 
// найдем максимальное и минимальное значения
// функции на отрезке [x1,x2]
 
y1:=f(x1); // минимум
y2:=f(x1); //максимум
x:=x1;
repeat
y := f (x);
if y < y1 then y1:=y;
if y > y2 then y2:=y;
x:=x+dx; until (x >= x2);
// вычислим масштаб
my:=h/abs(y2-y1); // масштаб по оси Y
mx:=w/abs(x2-x1); // масштаб по оси X
x0:=1;
y0:=b-Abs(Round(y1*my)) ;
with form1.Canvas do
begin              // оси
MoveTo(l,b);LineTo(l,b-h);
MoveTo(x0,y0);LineTo(x0+w,y0);
TextOut(l+5,b-h,FloatToStrF(y2,ffGeneral,6,3));
TextOut(l+5,b,FloatToStrF(y1,ffGeneral,6,3));
// построение графика
x:=x1; repeat
y:=f(x);
Pixels[x0+Round(x*mx),y0-Round(y*my)]:=clRed;
x:=x+dx;
until (x >= x2);
end;
end;
 
 
procedure TForm1.FormPaint(Sender: TObject); 
begin
GrOfFunc; end;
// изменился размер окна программы
 
procedure TForm1.FormResize(Sender: TObject); 
begin
// очистить форму
form1.Canvas.FillRect(Rect(0,0,ClientWidth,ClientHeight));
// построить график
GrOfFunc;
end;
end.

Программа хорошо выводит графики, когда функция имеет как положительные, так и отрицательные значения. Причем весь график помещается в указанном прямоугольнике.

Теперь посмотрим, а сколько вычислений значений функции делает программа? В данном случае (25-0)/0.01=2500. Для любого прямоугольника вывода. Чем был обусловлен выбор шага dx? Скорее всего, непрерывностью линии графика. Который, кстати, так и остался прерывистым на некоторых участках, там, где функция меняется быстро. Борются с этим, уменьшая dx, причем чаще радикально - сразу в 10, и даже в 100 раз, доводя до 0.0001; меньше мне не приходилось встречать. А это 250000 вычислений функции. И графики все равно прерывистые. Благо компьютеры быстрые. Но вот если вычислять функцию, заданную неявно, то график будет строится помедленнее. Выберем прямоугольник вывода 600*400. Таким образом по горизонтали мы можем иметь только 600 значений. По оси У, соответственно, тоже. Вопрос: куда идут остальные 249400 результата вычислений? Часть идет на построение вертикальных отрезков прямых, соединяющих соседние ординаты, а львиную долю других поедает Round . Вот тебе и дискретный вывод. Отсюда вытекает, что функцию нужно считать в 600 точках, а отрезки вертикальных прямых можно нарисовать карандашом. И dx нужно выбирать в нашем случае (25-0)/600= 0,0416666. График получится самый качественный, какой только возможно получить. Затем, нет необходимости вычислять ее значение дважды .Можно раз, запомнив результат в массиве(Массив имеет размер не более разрешения монитора). В таких условиях скорость вывода не меняется.

Во-вторых, сам метод построения (вычисление значений функции с шагом dx) работает как фильтр, отсекая высокочастотные гармоники, т.е я хочу сказать, что если к функции f(x) добавить что-то вроде g(x)*sin(2*pi/dx*x), то результат вывода будет плачевным. Этот элемент никак не изменит предыдущий график. Хотя он может являться основным носителем информации о функции. И уж конечно очень непросто вывести на экран график дискретной функции (имеется в ввиду универсальными программами общего пользования, подобными приведенной). Если взять f(x)=2*Sin(x)*exp(x/5)+ exp(x*x)*sin(2*pi/dx*x), то данная программа второе слагаемое не заметит, хотя будет тратить время на расчет f(x)=2*Sin(x)*exp(x/5)* exp(x*x)*sin(2*pi/dx*x). А в этом случае график константы. Приведенная программа, как я упоминал, некорректно отобразит и его, но это же учебная программа. Поэтому претензий не предъявляем.

А вот если взять TAB MathGrapher 1.0 (распространена в Интернете) и просто ввести 5* Sin(200*pi*x), то мы получим чистый ноль. Вместо 5, понятно, можно написать любую функцию, да и вместо Sin(200*pi*x) любую периодическую с кратной частотой, и программа выдаст неверный график.

Это, как я уже писал, издержки метода построения. Как с этим бороться? Неэффективный и не исправляющий программу брать dx=0.0037 или что-то наподобие, тогда сложно будет подобрать период и натыкать автора(шутка). Очевидно, нужно, чтобы программа давала возможность рассматривать функцию на небольшом отрезке, с dx=(х2-х1)/w (ширина вывода в пикселях). Т.е. программа должна автоматически менять шаг с уменьшением отрезка. Что-то подобное я написал, в результате можно графическим методом решать умопомрачительные трансцендентные уравнения с точностью 10E-7 и выше за считанные минуты (х2, х1 нужно вводить вручную). Тем, кто над подобным не задумывался, код в руки, а пользователям хотелось бы порекомендовать: не берите программы, которые не меняют шаг. А то в ответственных случаях можно получить неверный график и попасть в затруднительное положение.

 
« Пред.   След. »

Ivanovo State University of Chemical Technology has entered into an academic partnership with Visual Paradigm to better facilitate the teaching of software design & modeling through the use of Visual Paradigm.
Enterprise Architect
Sparx Systems Enterprise Arctitect provides Ivanovo State University of Chemical Technology with Enterprise Architect, Eclipse Integration, Visual Studio Integration, SysML Technology, Zachman Framework and much more for use in educational purposes, offered by the Enterprise Architect Academic Site License.