Рассмотрим еще один небольшой пример построения графика функции: “анимируем” вычисление экспоненты. Наша цель — дать вам почувствовать математические функции, продемонстрировать применение графиков для иллюстрации вычислений, показать фрагменты кода и, в заключение, предупредить о типичных проблемах, связанных с вычислениями.
Один из способов вычисления экспоненты сводится к суммированию степенного ряда.
ex
Чем больше членов ряда мы вычислим, тем точнее будет значение ex
exp0(x) = 0 // нет членов
exp1(x) = 1 // один член
exp2(x) = 1+x // два члена ; pow(x,1)/fac(1)==x
exp3(x) = 1+x+pow(x,2)/fac(2)
exp4(x) = 1+x+pow(x,2)/fac(2)+pow(x,3)/fac(3)
exp5(x) = 1+x+pow(x,2)/fac(2)+pow(x,3)/fac(3)+pow(x,4)/fac(4)
...
Каждая функция немного точнее приближает ex
pow(x,n)
— стандартная библиотечная функция, возвращающая xn
. В стандартной библиотеке нет функции, вычисляющей факториал, поэтому мы должны определить ее самостоятельно.int fac(int n) // factorial(n); n!
{
int r = 1;
while (n>1) {
r*=n;
––n;
}
return r;
}
Альтернативная реализация функции fac()
fac()
, можем вычислить double term(double x, int n) { return pow(x,n)/fac(n); } // n-й
// член ряда
Имея функцию term()
n
членов.double expe(double x, int n) // сумма n членов для x
{
double sum = 0;
for (int i=0; i
return sum;
}
Как построить график этой функции? С точки зрения программиста трудность заключается в том, что наш класс Function
expe()
имеет два аргумента. В языке С++ нет элегантного решения этой задачи, поэтому пока воспользуемся неэлегантным решением (тем не менее, см. упр. 3). Мы можем удалить точность n
из списка аргументов и сделать ее переменной.int expN_number_of_terms = 10;
double expN(double x)
{
return expe(x,expN_number_of_terms);
}
Теперь функция expN(x)
expN_number_of_terms
. Воспользуемся этим для построения нескольких графиков. Сначала построим оси и нарисуем истинный график экспоненты, используя стандартную библиотечную функцию exp()
, чтобы увидеть, насколько хорошо она приближается функцией expN()
.Function real_exp(exp,r_min,r_max,orig,200,x_scale,y_scale);
real_exp.set_color(Color::blue);
Затем выполним цикл приближений, увеличивая количество членов ряда n
for (int n = 0; n<50; ++n) {
ostringstream ss;
ss << " приближение exp; n==" << n ;
win.set_label(ss.str());
expN_number_of_terms = n;
// следующее приближение:
Function e(expN,r_min,r_max,orig,200,x_scale,y_scale);
win.attach(e);
win.wait_for_button();
win.detach(e);
}
Обратите внимание на последний вызов detach(e)
e
класса Function
ограничена телом цикла for
. Каждый раз, кода мы входим в этот блок, мы создаем новый объект e
класса Function
, а каждый раз, когда выходим из блока, объект e
уничтожается и затем заменяется новым. Объект класса Window
не должен помнить о старом объекте e
, потому что он будет уничтожен. Следовательно, вызов detach(e)
гарантирует, что объект класса Window
не попытается нарисовать разрушенный объект.На первом этапе мы получаем окно, в котором нарисованы оси и “настоящая” экспонента (синий цвет).