Версия для печати


Что нужно знать программисту и пользователю, работающему с программами, строящими графики функций
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=1415

Алексей Легкунец
дата публикации 20-09-2009 12:56

Что нужно знать программисту и пользователю, работающему с программами, строящими графики функций

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


Рис. 1

Немного о масштабе. Масштаб по осям может быть любым, равномерным как на Рис. 1, а также и неравномерным, например логарифмическим, экспоненциальным и др. В общем случае масштабы по осям различны. Какой применять в конкретном случае решается визуально, но критерий всегда один. График должен быть информативен и нагляден. Далее берём функцию, например такую у = х/2-sin(x). И вычисляем с шагом, например, 1 значения функции в нашем случае от -10 до 10. После продолжительных и однообразных вычислений мы, наконец, получим таблицу

x-10-9-8-7-6-5-4-3-2-10
y-5,5-4,1-3,0-2,8-3,3-3,5-2,8-1,4-0,10,30
x12345678910
y-0,30,11,42,83,53,32,83,04,15,5

и затем график см. Рис. 2.


Рис. 2

Сразу же мне захотелось этот процесс автоматизировать. Данная красная кривая и является графиком вышеупомянутой функции. На бумаге все просто и понятно. Скажу почему. Здесь мы не задумывались о числе точек, соседние соединяли плавной кривой, и вообще не чувствовали себя в рамках каких-либо ограничений. Совсем другое дело, если мы хотим нарисовать тот же график на экране монитора. Сейчас поясню. Монитор — это дискретное устройство. Оно имеет конечную ширину и высоту. И между любыми двумя точками имеется не бесконечно много точек, а наоборот конечное, и по математическим меркам, небольшое число так называемых элементов изображения — пикселов.

Попробуем сначала перенести график этой функции на прямоугольник 21х21 (пиксел). Вопрос, а в скольких точках нужно в данном случае считать функцию? Ответ очевиден в 21 (на Рис. 2 их столько же). Т.е. 1 пиксела — одно вычисление функции. Или, цена 1 пикселы равна шагу аргумента(dx). Посмотрим, как можно отобразить отрезок графика между двумя абсциссами, например Х=9 и Х=10. На экране монитора эти точки, с абсциссами 9 и 10, будут стоять в соседних вертикальных рядах, а значит, отрезок графика превратится в вертикальный на экране монитора.

Отсюда вытекает вполне естественный алгоритм построения на экране монитора. Функцию нужно считать столько раз, какова ширина прямоугольника вывода (в пикселах), а полученные точки соединить вертикальными прямыми.

Я привожу рисунок промежуточного результата с целью познакомить вас с возникшими проблемами. См. Рис. 3. Первое на что я обращаю ваше внимание, это то, что координаты всех точек которые можно отобразить, имеют целочисленные значения ( в нашем примере!). А это значит, что точки с промежуточными координатами мы на экране расположить не можем. Что делать? Ведь уже первая точка графика не может быть отображена. Выход найден и очень простой. Округляют значения координат точек до ближайшей, которая есть на экране. Первую точку нашего графика, ее ордината у=-5.5, расположим на месте точки с ординатой -6. Т.е. значение ординаты пришлось округлить до ближайшей точки, которая есть на экране, ее координаты (-10;-6). Другими словами мы немного искажаем график.


Рис. 3

Проделав вышеуказанные построения, мы получим точки графика (окрашены красным). Чтобы график был непрерывным, мы добавим еще четыре точки (окрашены желтым). Как видим, эти точки расположены по вертикали. Добавление их вполне законно, согласно описанию выше.

Теперь прежде чем продвигаться далее, возьмем на вооружение следующий факт, который мы обнаружили в процессе построения. А именно, выбрав масштаб по оси Х и У, плюс прямоугольник вывода (в пикселах) на экране монитора, мы тем самым фактически задали координаты всех точек этого прямоугольника, которые имеются физически. Посмотрим, какие координаты они имеют. Немного легкой математики.

Пусть Х1 , Х2 — начало и конец отрезка оси абсцисс, где рассматривается функция, w и h размеры прямоугольника вывода — ширина и высота. Вычислим шаг по оси Х dx=(Х2-Х1)/w; по оси У dy=(Ymax-Ymin)/h; Ymax,Ymin-максимальное и минимальное значение функции на участке Х1-Х2. Теперь на Рис. 4 видим, какие координаты можно присвоить пикселам на экране. Здесь же я указал красным прямоугольник размером dx*dy, все точки которого преобразуется в одну и туже пикселу, которая нарисована в середине.


Рис. 4

Сделаем такой вывод: Любую точку графика, на экране монитора, можно расположить с неопределенностью dx*dy. (Мне это нравится, как в квантовой механике.)

Теперь можно ответить на вопрос, который уже наверно созрел у читателя. А для чего нам, пользователям, это надо? Отвечу вопросом на вопрос. Кривая касается оси абсцисс. Можно ли утверждать, что касание есть реально? Теперь, зная, как обстоят дела с отображением точек, многие не станут это утверждать. Вспоминая неопределенность расположения точки на экране. Мы лишь можем сказать, что точка оси и точка графика попали в один и тот же прямоугольник dx*dy . А если dy будет большим числом, то можно ошибиться и весьма сильно.

А если взять прямоугольник вывода больше, что тогда делать? Сразу скажу, что строить как на бумаге не надо. И группировать пикселы по несколько штук в один шаг вычислений тоже не надо. У нас в руках компьютер, и мы свободно можем вычислить функцию одну — две и даже 10 тысяч раз, а точнее w раз, с шагом (Х2-Х1)/w. Х1,Х2— начало и конец отрезка оси абсцисс, где рассматривается функция, w — ширина поля вывода функции в пикселах (не больше ширины монитора).

Сейчас несколько слов о качестве графика. Легко заметить, что график будет непрерывной кривой. Это хорошо. Но чем больше расстояние между точками, в которых вычисляется значения функции, тем больше информации мы теряем. Если взять интервал построения большим, а прямоугольник вывода напротив маленьким то полученная кривая вряд ли будет иметь с графиком функции что-либо общее. В таком режиме можно рассмотреть лишь асимптоты, если они есть. Значит, качество зависит от величины прямоугольника вывода, и чем меньше отрезок, на котором рассматривается график функции. Это и понятно ведь детализация графика увеличивается. Еще раз подведу итог: качество графика тем выше, чем больше прямоугольник вывода и чем меньше участок оси абсцисс, на котором рассматривается функция.

Другими словами, чем меньше неопределенность dx*dy, тем график точнее. Сравните графики на Рис. 2 и 3. Разница в качестве очень большая. Это за счет того, что поле вывода на Рис. 2, примерно в 20 раз больше(400х400 против 21х21).

Итак, первое, что должен сделать пользователь, это увеличить окно вывода до максимальных размеров.

Теперь хочу разъяснить, что я буду иметь ввиду, говоря — детализация изображения. Детализация изображения — это улучшение качества графика. А оно получается за счет уменьшения отрезка, на оси абсцисс и (или) увеличением размеров окна вывода. Т.е. рассматривается часть графика в нужном месте отсюда и детализация.

А теперь несколько слов о методе, с помощью которого мы чертим графики.

Первое что мы сделаем, рассмотрим в нашем случае функцию. Y=sin(2*pi*x) и вычислим ее в точках показанных на Рис. 2. Мы видим, что значения этой функции в этих точках равно нулю. Поэтому если мы прибавим эту функцию к нашей, график которой был построен ранее, то при тех же условиях, мы не увидим разницы в графиках, т.е. результирующий график будет неверным. То же произойдет, если мы умножим эти функции — график опять будет неверным. Очевидно, что таких функций можно подобрать бесконечно много. Как легко заметить, приведенная функция есть высокочастотная гармоника sin(x). Это издержки метода построения. Борьба с ними проста, нужно просто изменить шаг (dx) вычисления функции — т.е. просто изменить размеры окна и если график изменится, то перейти к дальнейшей детализации изображения. ( А если это не помогает, то скорей всего ваша программа не меняет шаг автоматически и, следовательно, не отображает быстроменяющиеся функции. См. ниже. )

Рассмотрим некоторые особенности отображения периодических функций

Рассмотрим функцию sin(x). Всем известна из курса математики. На Рис. 5 приведён график одного периода этой функции, хорошего качества. Сколько периодов функции можно уместить в окне, чтобы можно было хотя бы понять, что это график синусоиды?


Рис. 5

Увеличивая интервал рассмотрения до 400 единиц, я получил

следующий Рис. 6. У меня получилось по горизонтали 12 пиксел на период.


Рис. 6

Далее уже трудно ориентироваться в изображаемом графике. Итак 12 пикселов на период, это минимум, который кстати еще не всем понравиться. Теперь смотрите, я еще увеличу интервал до 4850 единиц. И вот что получилось. Смотрите Рис. 7. Синусоида, качество хорошее. Вот только период странный, какой то. Может программа что-то ошибается. Смею Вас заверить, что программа тут не причем. И данный график скорее норма, чем ошибка. Просто нужно понять, в чем дело. А дело вот в чём. С увеличением отрезка (Х1, Х2) возрастает dx=(X2-X1)/w; И когда dx станет немного больше периода функции то на экране появится не основная синусоида, а ее субгармоника. С еще большим увеличением отрезка (Х1, Х2) dx станет немного больше двух периодов и мы снова увидим синусоиду но с другим периодом и т.д., кстати период может быть любым. Давайте экспериментировать. Я хочу получить, например, период субгармоники полностью занимающей окно. У меня w=765 примерно, нужно вычислить Х2. Берем dx=2*pi+2*pi/w=6.29, X2=w*dx=4801, вводим число в программу и получаем примерно то, что хотели. Смотрите Рис. 8. Небольшое не соответствие я отношу на счет приближенных вычислений, в том числе и к w.


Рис. 7
Если взять dx больше двух периодов то получим dx=4*pi+2*pi/w=12.57, X2=w*dx=9600. В районе этой точки мы получим, хоть и синусоиду но это будет не график функции.

Рис. 8

Вообще, превысив отрезок в 400 единиц, мы не сможем разглядеть нужный график. Когда мы увеличиваем интервал контроль за графиком все же есть. Совсем другая ситуация когда частота синусоиды большая. Прежде чем развернуть такой график, уменьшая интервал мы столкнемся (во всяком случае, можем) с субгармониками, которые важно не принять за график. Узнать подмену легко, нужно просто изменить размер окна, если картина не изменится, то мы нашли, то, что нужно. Для программистов — при изменении размеров окна, программа, должна построить график снова, с новым dx.

При изучении данного вопроса я использовал программу GrFunkcii (www.GrFunkcii.narod.ru). Программа написана на Delphi. Это программа на русском языке, с русской справкой. Поддерживает все элементарные функции в разных транскрипциях. Имеет предельно простой интерфейс. Кстати в неё встроена возможность вычислять корни уравнений с большой точностью, что само по себе очень ценно. Один клик мышкой — один корень.

Требования к программам, выводящим графики функций

Из сказанного выше вытекают несколько требований к программе выводящей графики функций.

Первое условие: программа должна понимать и уметь считать элементарные функции и их всевозможные комбинации.

Второе условие: Программа должна уметь менять шаг dx, согласно выбранным параметрам окна и отрезка Х1, Х2.

Поясню еще раз необходимость этого условия. Программы с постоянным шагом не могут отображать быстроменяющиеся функции. Их предел меньше чем 1/dx. Т.е. sin(2*pi*1/dx*x) и другие с частотой выше они отобразить не могут. Пример. Если dx=0.01, то частоты отображаемых функций не более 100 периодов на единицу. А значит гармонический анализ функций, ряды Фурье, по боку, и не только это. Выяснить, правильно ли выведен график, в отдельных случаях, не удастся (см. выше). А представляете, сколько вычислений функции надо сделать, чтобы построить график на интервале (0;4801) очевидно 480100 раз, и что самое обидное, все эти значения попадут в пресловутый прямоугольник dx*dy и превратятся в те несколько нужных точек.

Третье условие: Программа должна быть очень проста в употреблении, иначе лучше Maple или что-то в этом роде.

Четвертое условие: Программа должна быть дешевой. Пора бесплатного софта, хоть медленно, но уверенно кончается.

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