- •Ввод и вывод числовых значений
- •Логические выражения
- •Знаки отношения
- •Логические операции
- •Построение циклов при помощи условных операторов
- •Оператор цикла for
- •Оператор цикла с постусловием
- •Лабораторная работа №5. Составить программу, выполняющую суммирование ряда:
- •Типы данных
- •Целые числа
- •Вещественные числа
- •Символы
- •Массивы
- •Двумерные массивы
Оператор цикла for
Вновь вернемся к рассмотренным выше примерам, но теперь решим соответствующие задачи при помощи операторов цикла. Суммирование чисел от N1 до N2 легко выполнить при помощи следующей программ С++, использующей оператор цикла for:
С++
sum=0; for(x=N1;x<=N2;x++) sum+=x; |
В программе на С++ указывается начальное значение переменной х, условие, при выполнении которого цикл продолжается и способ изменения значения переменной цикла х. И в этом случае проверка выполнения условия производится в начале цикла, что позволяет прервать его, не начиная.
Сравнивая приведенные выше программы, в которых те же задачи решались при помощи условного оператора, с программой, использующей оператор цикла for, можно видеть несомненные преимущества оператора цикла. Запись цикла получается теперь гораздо компактнее и нагляднее, чем раньше.
В приведенной только что программе на С++ в цикле выполняется тот единственный оператор, который непосредственно располагается вслед за оператором цикла. Если требуется выполнение одновременно нескольких операторов, то их объединяют в один составной при помощи операторных скобок.
Например, будем вычислять одновременно сумму всех целых чисел от N1 до N2 и сумму квадратов этих же чисел. Это можно сделать при помощи следующей программы:
С++
sum1=0; sum2=0; for(x=N1;x<=N2;x++) { sum1+=x; sum2+=x*x; } |
Точка с запятой после закрывающей скобки } в программе на С не ставится.
Главным предназначением оператора цикла for является работа с целочисленными переменными или, более широко, с дискретными типами данных. Более подробно к этому вопросу мы еще вернемся, а пока что отметим, что на языке С, в принципе, допускается использование оператора for и с вещественными типами данных. Однако это не рекомендуется, поскольку для действий с вещественными числами существуют другие, более подходящие, операторы цикла.
Оператор цикла с постусловием
Вернемся к задаче о суммировании геометрической прогрессии, решенной ранее при помощи использования условного оператора. И в этом случае использование специальных операторов цикла существенно упрощает задачу. Начнем с оператора цикла с постусловием, т.е. с проверкой условия выхода из цикла в его конце. На С++ соответствующая программа выглядят следующим образом:
С++
sum=1; y=x; do{ sum+=y; y*=x; }while(fabs(y)>=1e-17); |
Обратим внимание на то, что в цикле С++ проверяют условие продолжения цикла.
В программе на С++ все операторы, расположенные между служебными словами do и while дополнительно берутся в операторные скобки {}.
Хотя это звучит несколько еретически, но в данном случае нельзя сказать, что специальный оператор цикла выглядят намного нагляднее и удобнее, чем сконструированные выше циклические программы с использованием условного оператора и оператора goto.
Оператор цикла с предусловием
Решим вновь все ту же задачу о суммировании геометрической прогрессии при помощи цикла с предусловием, т.е. с проверкой условия выхода из цикла в начале цикла. Эта программа имеет вид:
С++
Sum=1; y=x; while(fabs(y)>=1e-17) { sum+=y; y*=x; }; |
Операторы, выполняемые в цикле, объединяются в один оператор при помощи операторных скобок. В обоих случаях проверяется условие, при истинности которого цикл продолжается.
Суммирование степенных рядов
Выше мы рассмотрели пример геометрической прогрессии, как простейший пример вычисления бесконечной суммы. На самом деле, подобные вычисления в программировании занимают достаточно заметное место. Фактически, все известные элементарные функции (т.е. те функции, которые изучались в школе) требуют, для вычислений своих значений, суммирования бесконечных рядов. Рассмотрим некоторые типичные примеры.
Пример 1. Экспонента. Функция
,
где е2,71828182845905
вычисляется следующим образом:
Здесь 2!=12, 3!=123, 4!=1234,… При суммировании подобного ряда главное, как и в предыдущем случае, наилучшая организация вычисления очередного члена ряда. Хуже всего было бы вычислять для каждого члена ряда отдельно числитель, т.е. соответствующую степень х, и отдельно знаменатель, т.е. соответствующее произведение. Мы поступим следующим образом. Обозначим общий член ряда:
Тогда справедливо соотношение:
Действительно:
Таким образом, мы нашли рекуррентную зависимость, позволяющую вычислять следующий член ряда, если известен предыдущий. В программе мы будем опускать индекс у величины а, т.е. пользоваться зависимостью вида:
.
Теперь мы можем написать следующую программу на С++:
y=1;
k=1;
a=1;
do
{
a=a*x/k;
y=y+a;
k=k+1;
}
while (fabs(a)>=abs(y)*1e-20);
Эта программа вычисляет значение функции
у=
для одного заданного значения х. Вначале
переменные у, k и а получают
свои начальные значения (инициализируются).
Затем, при каждом проходе цикла,
вычисляется очередной член ряда а, его
значение прибавляется к величине у,
накапливая таким образом сумму ряда;
затем вычисляется номер следующего
члена ряда k и цикл
повторяется. Вычисления прекращаются
после достижения относительной
погрешности:
/
Это гарантирует правильность двадцати
разрядов мантиссы у результата у, так
как суммирование ряда прекращается
только после того, как очередной член
ряда становится меньше уже накопленной
суммы в
раз.
Пример 2. Однако, не всегда результаты вычислений бывают настолько очевидными. Рассмотрим, например, ряд, при помощи которого вычисляется функция sin x:
Обозначая через k=0, 1,… номер члена ряда можем записать рекуррентную зависимость для общего члена ряда:
Отбрасывая в обозначении общего члена ряда индекс, записываем следующую программу:
y:=x; a:=x; k:=1;
repeat
a:=-a*x*x/2/k/(2*k+1);
y:=y+a;
k:=k+1;
until abs(a)<=abs(y)*1e-20;
Внешне эта программа мало отличается от предыдущей. Однако, результаты вычислений по ней могут быть, в некоторых случаях, достаточно неожиданными.
На рисунке представлен график функции y=sin x, построенный на основе нашей программы.
Мы видим, что на протяжении нескольких периодов график имеет привычную форму, но затем приобретает хаотический вид. Рассмотрим причины этого явления. Дело в том, что в данном случае суммируемый ряд является знакопеременным. Соседние члены ряда имеют противоположные знаки. В итоге суммирование ряда фактически сводится к последовательности вычитаний. При достаточно больших значениях аргумента х (напомним, что х берется в радианах) происходит вначале значительное увеличение общего члена ряда, а только затем его уменьшение. В итоге значение функции sin x, которое не бывает больше единицы, получается за счет вычитаний чисел, значительно больших единицы. При этом теряются старшие разряды и резко возрастает погрешность вычислений. С ростом х эта погрешность становится настолько большой, что превосходит результаты вычислений. Это и приводит к хаотическому поведению графика, поскольку просуммированный ряд может дать любые, заведомо неверные, результаты.
Интересно по этому поводу заметить, что с точки зрения теории знакопеременные ряды считаются наиболее хорошо сходящимися. Однако теория предполагает вычисления с абсолютной точностью, в то время как компьютер выполняет вычисления с погрешностью. Как мы увидели на конкретном примере, эта погрешность может оказаться решающим фактором, делающим вычисления бессмысленными.
