Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
23
Добавлен:
03.03.2016
Размер:
2.87 Mб
Скачать

1. Формульно-словесный способ.

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

Пример 1. Найти

Шаг 1. Положить равным .

Шаг 2. Если , то положить равным .

Шаг 3. Если , то положить равным . Конец.

Пример 2. Найти наибольший общий делитель двух целых чисел .

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

Например, для = 420 и = 90 имеем

420 = 2  2  3  5  7; 90 = 2  3  3  5 .

Наибольший общий делитель в этом случае равен 2  3  5 = 30.

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

Более просто поставленная задача решается с помощью, так называемого алгоритма Евклида.

Обозначим наибольший общий делитель через . Вполне очевидно, что .

Тогда алгоритм Евклида можно описать следующим образом.

Шаг 1. Если = 0, то принять и закончить вычисления, иначе перейти к

шагу 2.

Шаг 2. Вычислить и .

Шаг 3. Заменить значение на значение , а значение - на значение . Пе-

рейти к шагу 1.

Здесь q - целая часть от деления m на n; r - остаток от деления.

При = 420, = 90 имеем:

Шаг 1. = 90  0;

Шаг 2. = [420/90] = 4; = 420 - 4  90 = 60;

Шаг 3. = 90; = 60;

Шаг 1. = 60  0;

Шаг 2. = [90/60] = 1; = 90 – 1  60 = 30;

Шаг 3. = 60; = 30;

Шаг 1. = 30  0;

Шаг 2. = [60/30] = 2; = 60 – 2  30 = 0;

Шаг 3. = 30; = 0;

Шаг 1. = 0  = 30 .

Основным недостатком формульно-словесной записи алгоритма является то, что здесь используется естественный язык, для которого органически присуща неоднозначность слов. К недостаткам данного способа относят также ненаглядность записи алгоритма.

2. Блок-схемный способ.

Блок-схема - это графическое изображение логической структуры алгоритма. Здесь каждый этап вычислительного процесса изображается в виде определенной геометрической фигуры, называемой блоком. Основными блоками являются:

- начало или конец алгоритма (овал);

- ввод или вывод данных (параллелепипед);

- обработка данных, т.е. вычислительный

процесс (прямоугольник);

- логический блок (ромб).

Связь между блоками изображают горизонтальными или вертикальными линиями. Излом линии связи делается только под прямым углом. При направлении линии связи слева направо или сверху вниз стрелку на линии можно не ставить; при направлении справа налево, снизу вверх или при наличии излома стрелка обязательна.

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

- адресные указатели

3. Алгоритмические языки применяются для записи алгоритмов в виде программ, предназначенных для реализации на ЭВМ.

О П Е Р А Т О Р П Р И С В А И В А Н И Я

Для исключения путаницы в понятиях "операция" и "оператор", отметим, что оператор - это наименьшая исполняемая единица программы. Очень часто в литературе по Си вместо термина "оператор" используется его синоним "инструкция". Операция a = c становится оператором, если в конце поставить точку с запятой a = c;. В Си точка с запятой является заключающим символом оператора. Различают операторы присваивания, действие которых состоит в вычислении и сохранении результатов заданных выражений (например: a = sin(b)+c; j++;), операторы объявления, составные операторы, пустые операторы, операторы метки, цикла и т.д. Для обозначения конца оператора в языке Си используется точка с запятой. Что касается составного оператора (или блока), представляющего собой набор логически связанных операторов, помещенных между открывающей ({) и закрывающей (}) фигурными скобками ("операторными скобками"), то за ним точка с запятой не ставится. Отметим, что блок отличается от составного оператора наличием определений в теле блока.

В блок-схемах обычная математическая запись , означающая, что переменная принимает значение выражения , представляется в виде (читается " становится равным " ). При выполнении этого оператора переменная теряет свое предыдущее значение и получает новое значение.

Каждое имя в программе (в том числе в блок-схеме) – это косвенное обозначение адреса поля памяти. Следовательно, при выполнении оператора в оперативной памяти будут использованы поля с именами

Процессор

a  x + b

y a b x

Память

Выполнение оператора присваивания производится в четкой последовательности во времени:

1) из памяти в процессор читаются значения переменных (содержимое полей памяти с адресами );

2) в процессоре вычисляется значение выражения ;

3) результат вычислений записывается в память в поле с именем .

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

Пример 1. Обменять значения переменных и .

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

b

a

2

R

1 3

Числа 1, 2, 3 указывают последовательность пересылок содержимого полей памяти , и .

Представленную выше схему можно реализовать такими операторами:

; ; .

Подчеркнем еще раз смысл оператора присваивания. Если в правой части оператора записана переменная, например,

,

то это означает, что содержимое поля памяти с именем (адресом) пересылается в поле . Разумеется, такая пересылка осуществляется через процессор.

Если в правой части оператора присваивания стоит выражение, например,

,

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

Если в левой части оператора присваивания записаны имена двух переменных, например, , то переменной присваивается значение выражения , как показано выше. После этого в поле памяти записывается значение из поля памяти .

В Си оператор присваивания имеет такое же обозначение, как и на блок-схеме. Его синтаксическая диаграмма:

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

Пример 2.

(25 – 3*3)/(3 + 1) * 7 – 4 + 2*3 = (25 – 9)/4 * 7 – 4 + 6 = 16/4 * 7 + 2 =

4 * 7 + 2 = 28 + 2 = 30

Некоторую особенность имеет вычисление логических операций && и || (логические И и ИЛИ), для которых в Си применяется так называемая короткая схема вычислений. По этой схеме вначале вычисляется первый операнд. Если для операции && получено при этом значение false, то второй операнд не вычисляется, так как результат операции все равно будет иметь значение false. Аналогично, если первый операнд операции || имеет значение true, то второй операнд не вычисляется. Например, в выражении (x<0) && (y>x-1) при x = 1 будет вычислен лишь первый операнд и получено общее значение false вне зависимости от значения, которое имеет переменная y.

Оператор присваивания выполним, если переменная и выражение совместимы по присваиванию. Основные принципы совместимости по присваиванию:

1) переменная и выражение относятся к одному типу.

2) тип выражения преобразуется к типу переменной.

Пример 3.

float a,b;

int m,n;

a=55.85; m=15;

b=m; n=a;

В операторе n = a имеет место потери точности, так как переменной n можно присвоить лишь целую часть значения переменной a (при присваивании b=m потери точности не происходит). В результате получим b=15.0, а n=55.

У С Л О В Н Ы Й О П Е Р А Т О Р

С помощью условного оператора реализуются разветвляющиеся вычислительные процессы.

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

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

Синтаксическая диаграмма условного оператора:

Керниган - 59

Выражение в скобках после if является логическим выражением. Если значение выражения истинно (т. е. отлично от нуля), то выполняется оператор 1. Если выражение ложно (т. е. его значение равно нулю) и существует else-часть, то выполняется оператор 2, а при отсутствии альтернативы else не выполняется никаких действий.

В примерах по определению y = |x| и x = |x| показано применение оператора if с фразой else и без нее.

y = |x| x = |x|

да нет да нет

if (x<0) if (x<0)

y=-x; x=-x;

else

y=x;

В первом случае в переменную y необходимо записать значения x или x, в зависимости от значения x. Во втором случае требуется изменить знак переменной x, если ее значение отрицательное, else-часть в этом операторе отсутствует.

Если в какой-либо ветви необходимо выполнить несколько операторов, их нужно ограничивать фигурными (операторными) скобками, в этом случае все они воспринимаются компилятором, как один оператор.

да нет

да нет

а) б)

На рис. а) представлена блок-схема оператора if с операторными скобками, при выполнении которого операторы x=-x; и y=x; будут исполняться только при условии x<0.

if (x<0)

{

x=-x;

y=x;

}

Если операторные скобки не поставить:

if (x<0)

x=-x;

y=x;

то оператор y=x; будет исполняться независимо от значения x рис. б). Такие же рассуждения применимы и к ветви else.

Если в операторе:

if (x<0)

{

x=-x;

y=x;

}

else

y=x;

не проставить операторные скобки, компилятор определит ошибку в синтаксисе программы. В этом случае в операторе if при x<0 выполняется оператор x=-x;. Так как далее фраза else не встречается, то компилятор определяет, что оператор if завершился. Затем должен выполняться оператор y=x;, вне зависимости от значения x. После этого встречается ключевое слово else. Это является ошибкой, поскольку с этого слова оператор начинаться не может.

Операторы 1 и 2, изображенные на синтаксической диаграмме условного оператора, в свою очередь могут быть условными операторами.

if (B1)

if (B2)

if (B3)

S1;

else

S2;

else

if (B4)

S3;

else

S4;

else

S5;

Здесь B1,B2,B3,B4 – некоторые логические выражения, принимающие значения соответствующие true или false; S1,S2,S3,S4,S5 – операторы (например, операторы присваивания). Каждое слово "else" в условном операторе относится к ближайшему слову "if", не связанному с каким-либо словом "else".

Схема действия записанного выше условного оператора наглядно изображена на приведенной ниже блок-схеме.

false true

false true

false true false true

Условные операторы, многократно вложенные друг в друга, трудно понимать и в программах их применять не рекомендуется.

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

if (n > 0)

if (а > b)

z = a;

else

z = b;

else относится к внутреннему if, что показано с помощью отступов.

да нет

да нет

Если требуется иная интерпретация, необходимо должным образом расставить фигурные скобки:

if (n > 0)

{

if (а > b)

z = a;

}

else

z = b;

да нет

да нет

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

Пример 1.

Это сигнатура, функция знака y = sign(x). График функции:

y

1

0 x

-1

Блок-схема вычисления функции знака:

Здесь производятся две проверки. В первой из них () определяется первая ветвь вычислений, но остаются неопределенными вторая и третья ветви в формуле вычисления функции знака. Разделение этих ветвей выполняет вторая проверка ().

да нет

да нет

Си-программа вычисления функции знака:

int main()

{

int x;

char y;

//Ввод значения аргумента x

if ( x > 0 )

y = 1;

else if ( x < 0 )

y = -1;

else

y = 0;

//Печать результата

getch();

return 0;

}

Замечание 1.

Конструкция

if (выражение)

инструкция

else if (выражение)

инструкция

else if (выражение)

инструкция

else if (выражение)

инструкция

else

инструкция

встречается так часто, что о ней стоит поговорить особо. Приведенная последовательность инструкций if - самый общий способ описания многоступенчатого принятия решения. Выражения вычисляются по порядку; как только встречается выражение со значением "истина", выполняется соответствующая ему инструкция, на этом последовательность проверок завершается. Здесь под словом инструкция имеется в виду либо одна инструкция, либо группа инструкций в фигурных скобках.

Последняя else-часть срабатывает, если не выполняются все предыдущие условия. Иногда в последней части не требуется производить никаких действий, в этом случае фрагмент

else

инструкция

можно опустить или использовать для фиксации ошибочной ("невозможной") ситуации.

Замечание 2.

При проверке условия if ( x < 0 ) уже точно установлено, что перемененная x не может быть положительной. Она может быть нулевой или отрицательной (отличной от нуля). Это выражение может быть заменено на следующее if ( x != 0 ).

if ( x > 0 )

y = 1;

else if ( x != 0 )

y = -1;

else

y = 0;

Так как if просто проверяет числовое значение выражения, условие можно записать в сокращенном виде if ( x ).

if ( x > 0 )

y = 1;

else if ( x )

y = -1;

else

y = 0;

Таким образом, запись

if (выражение)

получается короче, чем

if ( выражение != 0 )

Иногда такие сокращения естественны и ясны, в других случаях, наоборот, затрудняют понимание программы.

Замечание 3

Если x - вещественная переменная, то сравнение в большинстве случаев нужно выполнять по , где - достаточно малое число (погрешность задания исходных данных). Как уже ранее указывалось, вещественные числа, как правило, определяют результаты измерений, производимых с определенной точностью. В этом случае значение с точки зрения математики является положительным числом. Если же переменная означает вес груза, то значение кг должно считаться эквивалентным нулю, если точность измерения веса кг.

На числовой оси вещественный нуль - это -окрестность точки 0. Другими словами, если x < , то x считается равным нулю.

x < 0 x = 0 x > 0

- 0  x

Следовательно, для вещественных переменных

Аналогично, и т.п.

Исходя из этих рассуждений, замена выражения if ( x != 0 ) на if ( x ) является ошибкой, так как при вычислении выражений вещественного типа получение результата точно равного 0 маловероятно. Однако, компилятор не определяет такую конструкцию, как ошибочную.

Реализация функции знака для вещественного аргумента:

int main()

{

const float eps = 0.00001;

float x;

char y;

//Ввод значения аргумента

if ( x > eps )

y=1;

else if ( x < -eps )

y=-1;

else

y=0;

//Печать результата

getch();

return 0;

}

Пример 2. Определить.

Ниже приведена блок-схема для определения максимальной из 3-х переменных.

да нет

да нет

В алгоритме переменной y присваивается значение переменной a. Затем y последовательно сравнивается с значениями b и c. В случае, когда значение b или c больше y, запоминается новый максимум.

Си-программа определения максимальной из 3-х переменных:

int main()

{

double a,b,c,y;

//Ввод значения a,b,c

y=a;

if (b>y)

y=b;

if (c>y)

y=c;

//Печать y

getch();

return 0;

}

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

Пример 3. Расположить значения в переменных a,b,c в порядке возрастания.

Для решения задачи упорядочивания значений (сортировки) в порядке возрастания необходимо в переменную a занести минимальной значение, а в переменную c - максимальное. Решение разбивается на 3 шага.

Шаг 1. Сравниваются значения a,b. Если a>b, то значения в переменных a,b обменять местами.

Шаг 2. Сравниваются значения a,c. Если a>c, то значения в переменных a,c обменять местами. (После этих шагов в переменной a будет минимальное значение.)

Шаг 3. Сравниваются значения b,c. Если b>c, то значения в переменных b,c обменять местами.

Пусть введены значения a,b,c равные 5,9,2. В таблице показано, как будут изменяться значения в переменных при выполнении 3-х шагов.

До обмена

После обмена

a

b

c

a

b

c

Шаг 1

5

9

2

5

9

2

Шаг 2

5

9

2

2

9

5

Шаг 3

2

9

5

2

5

9

Блок-схема алгоритма:

да нет

да нет

да нет

Си – программа сортировки значений в 3-х переменных.

int main()

{

double a,b,c,buf;

//Ввод значений a,b,c

if (a>b)

{ buf=a; a=b; b=buf; }

if (a>c)

{ buf=a; a=c; c=buf; }

if (b>c)

{ buf=b; b=c; c=buf; }

//Печать a,b,c

getch();

return 0;

}

Операторы { buf=a; a=b; b=buf; } являются составным оператором, расстановка скобок {} – обязательна.

Для сортировки значений в порядке убывания при сравнении значений переменных знак “>” изменяется на знак “< ”, а схема алгоритма остается неизменной.

При сортировке значений в 4-х переменных a,b,c,d алгоритм можно разбить на 3 прохода:

В общем случае для n переменных количество проходов будет равно (n-1). Количество шагов на каждом i-м проходе будет (n-i). Для оценки производительности общее количество шагов K определяется по формуле:

Пример 3. Определить, является ли треугольник a,b,c равносторонним, равнобедренным или разносторонним.

При решении задач, которые имеют геометрический, физический или любой другой смысл, необходимо проверять на корректность введенные данные. Если данные введены не корректно, решение не будет иметь смысла. Например, треугольник не будет существовать, если любая из сторон будет меньше или равно нулю. Также, любая сторона должна быть меньше суммы двух других сторон. Любая формула, известная в геометрии, справедлива только в том случае, когда треугольник существует. Проверять каждое из 6-и условий в отдельной условной вершине не имеет смысла, блок-схема и программа будет пересыщена проверками. Поэтому условия проверяются в одной вершине и объединяются в логическое выражение операциями И, ИЛИ, НЕ (отрицание). Если треугольник не существует, то выводится соответствующее сообщение. Это сообщение является обязательным, исходя из свойства алгоритма – результативность.

После проверки существования треугольника определяется, является ли он равносторонним. Если выполняются условия a==b и b==c, то будет выполняться и a==c. Поэтому в условной вершине проверяются только 2 условия.

Если треугольник не равносторонний и в нем имеется пара равных сторон, то он будет равнобедренным. Должно выполняться одно из условий a==b, b==c или a==c. Это достаточное условие равнобедренности треугольника. Условие равнобедренности должно проверяться обязательно вторым в очереди, после условия равносторонности. Порядок проверки важен. Если эти две проверки переставить местами по очередности, то в алгоритме никогда не будет определено, что треугольник равносторонний. Если все 3 стороны равны, то будет равна любая пара сторон, и треугольник определится, как равнобедренный.

Если не выполняется ни одно из 2-х условий, то треугольник будет разносторонним.

В решении предполагается, что переменные целочисленные. Если переменные будут вещественные, что в большей степени соответствует реальности, то все сравнения надо производить с учетом погрешности ..

Блок-схема решения:

нет да

да нет

да нет

Си-программа:

int main()

{

int a,b,c;

//Ввод значений a,b,c

if ( a>0 && b>0 && c>0 && a+b>c && a+c>b && c+b>a )

if ( a==b && b==c )

printf("\n Треугольник равносторонний\n");

else if ( a==b || b==c || a==c )

printf("\n Треугольник равнобедренный\n");

else

printf("\n Треугольник разносторонний\n");

else

printf("\n Треугольник не существует\n");

getch();

return 0;

}

О П Е Р А Т О Р П Е Р Е Х О Д А

Синтаксическая диаграмма:

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

Пример. Алгоритм Евклида.

int main()

{ // программа Evklid1

unsigned int m,n,d,q,r;

//Ввод значения m,n

m10:

if (n>0)

{

q = m / n; r = m % n;

m = n; n = r;

goto m10;

}

d = m;

//Печать d

getch();

return 0;

}

Примечание. Оператор q = m / n является избыточным, поскольку вычисленное значение q в программе Evklid1 не используется.

Рассмотрим вкратце, что собой представляет метка в программе.

При трансляции Си-программы каждый ее оператор преобразуется в одну или несколько машинных инструкций. Эти инструкции размещаются в последовательных полях памяти. Каждая инструкция имеет конкретный адрес, определяемый адресом крайнего левого байта поля памяти, которое занимает инструкция. Команды, как правило, выполняются последовательно, в порядке их записи в памяти. Однако если встречается инструкция перехода, которой соответствует оператор goto в Си-программе, то дальше будет выполняться не следующая по порядку машинная инструкция, а та, адрес которой указан в инструкции перехода. Этот адрес перехода и соответствует метке в Си-программе.

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

Ниже перечисляются случаи, когда целесообразно применять оператор goto:

  • выход из блока в вызывающую программу (переход с помощью оператора goto на конец блока);

  • переход на конец тела цикла;

  • принудительный выход из цикла;

  • переход к удаленному фрагменту программы.

Однако для того, чтобы явно не применять оператор goto, в Си для первых трех случаев предусмотрены специальные операторы, а именно, continue и break, а также функция exit.

В частности, алгоритм Евклида легко реализовать без операторов goto с помощью операторов цикла while или do while.

П У С Т О Й О П Е Р А Т О Р

В Си-программе точка с запятой является заключающим символом оператора. Поэтому если в программе поставить подряд две точки с запятой, то будет считаться, что между ними находится пустой оператор. Тот же эффект мы будем иметь:

- если после знака “}” в блоке или составном операторе поставить точку с запятой;

- если установить точку с запятой непосредственно после слова else или после выражения в операторе if и др.

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

Пустой оператор может появиться в программе случайно, вопреки желанию программиста. В приведенном фрагменте б) после выражения в операторе if случайно была поставлена точка с запятой. Компилятор не определяет это как ошибку, но логика работы программы изменяется существенно. В фрагменте а) определяется модуль значения x, а в б) - значение x меняет свой знак. Надо внимательно следить за расстановкой точек с запятыми, потому что такие ошибки трудно определять.

if ( x < 0 ) if ( x < 0 );

x = - x; x = -x;

y = x; y = x;

да нет да нет

(пустой оператор)

а) б)

В приведенном ниже фрагменте программы 4 пустых оператора.

int main()

{

double x, y, z;

x = -15.3; ;

if (x);

if (x>0)

{

y = x * x - 1;

m1: z = x + y;

};

m2: ;

}

О П Е Р А Т О Р Ц И К Л А С П Р Е Д У С Л О В И Е М

Циклом называется многократно выполняемый участок программы.

Обычно для организации циклических программ используют операторы цикла. В Си определены три таких оператора: оператор цикла с предусловием while, оператор цикла с постусловием do while и оператор цикла for.

Синтаксическая диаграмма оператора цикла с предусловием:

Схема выполнения оператора while:

В цикле вычисляется выражение. Если его значение отлично от нуля (true), то выполняется оператор, и вычисление выражения повторяется. Этот цикл продолжается до тех пор, пока выражение не станет равным нулю (false), после чего вычисления продолжатся с точки, расположенной сразу за оператором. Таким образом, оператор после слова while выполняется нуль или более раз. Чтобы цикл не был бесконечным (случай "зацикливания"), значение выражения в операторе должно изменяться таким образом, чтобы оно стало равным false.

Оператор цикла с предусловием наиболее часто применяют при реализации итерационных процессов.

Пример 1. Зацикливание программы.

x=10;

while (x>0)

{

y=x+1; z=y-2;

}

Пример 2. Алгоритм Евклида (Evklid2)

int main()

{ // программа Evklid2

unsigned int m,n,d,r;

//Ввод значения m,n

if (m<n)

{ r=m; m=n; n=r; }

while (n>0)

{ r=m % n; m=n; n=r; }

d=m;

//Печать результата

getch();

return 0;

}

В программе Evklid1 не учтено, что при вводе данных могут быть заданы значения m<n. В этом случае будет производиться лишнее деление. В программе Evklid2 сравниваются введенные значениям и приводятся к требуемому соотношению m>n.

Если в данном примере введено m = 10, n = 0, то цикл while ни разу не выполняется, и в результате получаем d = 10.

Блок-схема алгоритма.

Пример 3. Модификация алгоритма Евклида.

int main()

{ // программа Evklid2

unsigned int m,n,d;

//Ввод значения m,n

while ((m>0) && (n>0))

if (m>n)

m=m % n;

else

n=n % m;

d=m+n;

//Печать результата

getch();

return 0;

}

Программы Evklid2 и Evklid2a можно считать равноценными по быстродействию (компьютерный эксперимент показывает, что программа Evklid2a работает примерно на 7% быстрее по сравнению с программой Evklid2).

Пример 4. Определить, сколько раз число b является сомножителем числа a.

Если числа a или b равны 0, то задача не имеет решения. Определяется переменная kol, как счетчик количества вхождений числа b в сомножители числа a. Пока a делится на b без остатка, в цикле вычисляется частное, а к счетчику прибавляется 1. Перед циклом счетчик устанавливается в 0 (обнуляется).

Блок-схема алгоритма.

Си-программа.

int main()

{

unsigned int a,b,kol;

//Ввод значения a,b

if (a==0 || b==0)

printf("\n Нет решения \n");

else

{

kol=0;

while (a%b==0)

{

kol++; a=a/b;

}

}

//Печать kol

getch();

return 0;

}

В приведенном решении присутствуют 2 неточности:

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

2. В приведенной программе не учтен вариант, когда числа могут быть отрицательными. В этом случае надо работать с модулями введенных значений. Модуль целочисленной переменной возвращает функция abs(), которая объявлена в заголовочном файле “math.h”.

#include <math.h>

. . .

int main()

{

int a,b,kol,a1,b1;

//Ввод значения a,b

if (a==0 || b==0)

printf("\n Нет решения \n");

else

{

kol=0; a1=abs(a); b1=abs(b);

while (a1%b1==0)

{

kol++; a1=a1/b1;

}

}

//Печать kol

getch();

return 0;

}

Пример 5. Распечатать цифры целого числа a в обратном порядке.

В разделе «Системы счисления» рассматривался алгоритм получения цифр в 2,8,16-ой системах счисления. Если осуществлять деление на 10, будут выделяться 10-цифры из заданного числа в обратном порядке. Этот алгоритм является основой для решения задачи. Предварительно берется модуль числа, для обработки и отрицательных значений.

Блок-схема.

Си-программа

int main()

{

int a,d,a1;

//Ввод значения а

a1=abs(a);

while ( a1 != 0 )

{

d = a1 % 10; a1 = a1 / 10;

printf(" %1d", d);

}

getch();

return 0;

}

Недостаток приведенного решения заключается в том, что при а=0 ничего не будет напечатано.

О П Е Р А Т О Р Ц И К Л А С П О С Т У С Л О В И Е М

Синтаксическая диаграмма:

Между зарезервированными словами do и while располагается оператор, который завершается точка с запятой, или произвольное количество операторов, заключенных в операторные скобки. После знака ”}” точка с запятой не ставится. В частном случае здесь может быть лишь один пустой оператор.

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

В теле цикла do - while должно изменяться значение выражения, записанного после слова while, таким образом, чтобы это выражение, в конечном счете, приняло значение false, в противном случае будет иметь место зацикливание программы. В целом, цикл do while выполняется так же, как и цикл while. Отличие состоит в том, что операторы тела цикла do while выполняются до проверки условия продолжения цикла, поэтому они выполнятся, по крайней мере, один раз.

Схема выполнения:

Пример 1. Распечатать цифры целого числа a в обратном порядке.

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

Блок-схема.

Си-программа

int main()

{

int a,d,a1;

//Ввод значения а

a1=abs(a);

do

{

d = a1 % 10; a1 = a1 / 10;

printf(" %1d", d);

}

while ( a1 != 0 );

getch();

return 0;

}

Пример 2. Определить среднее арифметическое цифр целого числа a.

Для решения задачи вводятся две переменные sumdig (сумматор) для накапливания суммы цифр числа, которые выделяются по известному алгоритму, и koldig (счетчик) для подсчета количества цифр, к которому в каждом проходе по циклу прибавляется 1. Перед циклом эти переменные обнуляются. При определении среднего арифметического цифр s, используется преобразование sumdig к типу float для получения вещественного результата.

Блок-схема.

Си-программа

int main()

{

int a,a1,koldig,sumdig;

float s;

//Ввод значения а

a1=abs(a);

koldig=0; sumdig=0;

do

{

sumdig += a1 % 10;

koldig ++; a1 = a1 / 10;

}

while ( a1 != 0 );

s = (float)sumdig / koldig;

//Печать s

getch();

return 0;

}

Пример 3. Удалить из исходного числа a все цифры 5.

Блок-схема.

Пусть задано число 35475, после преобразования должно получиться число 347. Для решения задачи используется алгоритм выделения цифр числа. Из выделенных цифр формируется новое число с использованием весовых коэффициентов разрядов числа. Весовой коэффициент нулевого разряда равен 1. Так как выделение цифр начинается с младшей, то переменной p перед циклом присваивается значение 1.

После выделения очередной цифры, она умножается на весовой коэффициент и добавляется в сумматор s. Переменная p умножается на 10, для получения весового коэффициента очередного (старшего) разряда. Эти действия выполняются в том случае, если цифра не равна 5. Для заданного числа получим s = 7*1 + 4*10 + 3*100 = 347.

После получения s проверяется знак исходного числа, и в переменную a записывается значение s с определенным знаком.

Си-программа

int main()

{

int a,a1,digit,s,p;

//Ввод значения а

a1 = abs(a); s = 0; p = 1;

do

{

digit = a1 % 10; a1 = a1 / 10;

if ( digit != 5 )

{ s += digit * p; p *= 10; }

}

while ( a1 != 0 );

if ( a < 0 ) a = -s; else a = s;

//Ввод a

getch();

return 0;

}

Пример 4. Получить число b, в котором цифры исходного числа a переставлены в обратном порядке. Схема Горнера.

Полином (многочлен)

целесообразно всегда вычислять по схеме Горнера:

.

Схема Горнера обладает следующими преимуществами по сравнению с обычной записью полинома:

- меньшее количество операций умножения;

- меньшая погрешность вычислений;

- удобство реализации вычислений в виде цикла.

Для вычисления полинома по обычной схеме требуется операций умножения, по схеме Горнера - таких операций.

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

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

Вычисления в программе организуются по следующей методике:

P = an

P = P * x + an-1

P = P * x + an-2

...................

P = P * x + a1

P = P * x + a0

Если считать, что в приведенных формулах a0an это цифры числа, а x – основание степени счисления, например 10, то схему Горнера можно использовать для получения числа из его цифр. Цифра старшего разряда должна включаться в схему Горнера первой. При использовании рассмотренных алгоритмов выделения цифр из числа первой выделяется цифра младшего разряда. Если ее включить в схему Горнера первой, она станет цифрой старшего разряда. При дальнейшем выделении цифр и включении их в схему Горнера, в сформированном числе цифры исходного числа будут расположены в обратном порядке.

Максимальное значение числа типа int составляет 2147483647. Поэтому, если введенное число будет больше 1000000000, а младшая цифра, например, равняется 5, то при перестановке цифр в обратном порядке получится число, превышающее диапазон допустимых значений заданного типа (возникает переполнение). В итоге результат будет неверен. Потому в программе осуществляется проверка условия a1>1000000000.

Блок-схема.

Си-программа

int main()

{

int a,a1,digit,b;

//Ввод значения а

a1=abs(a);

if(a1>1000000000)

{ printf("Преобразование может вызвать переполнение\n");

getch();

exit(0);

}

b = a1 % 10;

while( a1 > 9 )

{

a1 = a1 / 10;

digit = a1 % 10;

b = b * 10 + digit;

}

if (a<0) b=-b;

//Печать b

getch();

return 0;

}

О П Е Р А Т О Р Ц И К Л А С П А Р А М Е Т Р О М

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

Пример 1. Вычислить путем многократного умножения ( - целое положительное число).

Здесь i – параметр цикла, т.е. переменная, которая управляет количеством его повторений. В подготовительном блоке параметру цикла назначается определенное начальное значение, модифицирующий блок изменяет значение параметра, управляющий блок проверяет условие окончания цикла.

Приведенный в блок-схеме алгоритм решения задачи легко реализовать с помощью цикла while.

Си-программа с циклом while.

int main()

{

int i,n;

double x,y;

//Ввод значения x,n

y=1.; i=1;

while(i<=n)

{y*=x; i++;}

//Печать y

getch();

return 0;

}

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

Тогда блок-схема задачи будет иметь вид:

Си-программа с циклом for

int main()

{

int i,n;

double x,y;

//Ввод значения x,n

y=1.;

for(i=1;i<=n;i++)

y*=x;

//Печать y

getch();

return 0;

}

Оператор for описывает цикл, который является обобщением цикла while. Если сравнить его с ранее написанным while, то станет ясно, как он работает. Внутри скобок имеются три выражения, разделяемые точкой с запятой. Первое выражение — инициализация i = 1 выполняется один раз перед тем, как войти в цикл. Второе — проверка условия продолжения цикла i <= n. Условие вычисляется, и если оно истинно, выполняется тело цикла. Затем осуществляется приращение шага: i++, и условие вычисляется снова. Цикл заканчивается, когда условие становится ложным. Как и в случае с while, тело for-цикла может состоять из одного оператора или из нескольких, заключенных в фигурные скобки. На месте этих трех выражений (инициализации, условия и приращения шага) могут стоять произвольные выражения.

Выбор между while и fоr определяется соображениями ясности программы. Цикл for более удобен в тех случаях, когда инициализация и приращение шага логически связаны друг с другом общей переменной и выражаются единичными инструкциями, поскольку названный цикл компактнее цикла while, а его управляющие части сосредоточены в одном месте.

Оператор for формально записывается, в следующем виде:

for (выражение_1; выражение_2; выражение_3) тело_цикла

1.Тело цикла составляет либо один оператор, либо несколько операторов, заключенных в фигурные скобки { ... } (после блока точка с запятой не ставится). В выражениях 1, 2, 3 фигурирует специальная переменная, называемая управляющей. По ее значению устанавливается необходимость повторения цикла или выхода из него.

2.Выражение_1 присваивает начальное значение управляющей переменной, выражение_З изменяет его на каждом шаге, а выражение_2 проверяет, не достигло ли оно граничного значения, устанавливающего необходимость выхода из цикла.

3.Выражение_2 проверяется перед исполнением тела цикла, а значит, если оно ложно, то тело цикла ни разу не исполнится.

4.Любое из трех выражений в цикле for может отсутствовать, однако точка с запятой должна оставаться. Таким образом, for ( ; ; ) {...} - это бесконечный цикл, из которого можно выйти лишь другими способами.

5.Любое из трех выражений в цикле for может быть операцией последовательного вычисления, состоящим их нескольких операций, разделяемых запятой. Приведенную программу можно записать в виде:

int main()

{

int i,n;

double x,y;

//Ввод значения x,n

for(y=1.,i=1;i<=n; y*=x,i++);

//Печать y

getch();

return 0;

}

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

В рассмотренном выше примере при n=10 и x=2 в результате получится y=1024 (y=xn, т.е. y=210). Если записать цикл в виде for(y=1.,i=1; y*=x,i<=n; i++), то ошибки при компиляции не будет. Однако, в результате получится y=2048.

Пример 2. Вычисление факториала y=n! =

Блок-схема

Си-программа

int main()

{

nsigned char i,n;

unsigned long long int y;

//Ввод значения n

y=1;

for (i=2;i<=n;i++)

y*=i;

//Печать y

getch();

return 0;

}

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

Пример 3. Вычисление двойного факториала y=n!!

Двойной факториал – это произведение четных чисел при четном значении переменной n или произведение нечетных чисел при нечетном n.

Блок-схема

Начальное значение параметра цикла устанавливается равным 2, если n четное, и равное 3 при нечетном значении n. В цикле применяется шаг, равный 2. В этом случае параметр цикла принимает только четные или нечетные значения.

Си-программа

int main()

{

unsigned char i,n;

unsigned long long int y;

//Ввод значения x,n

y=1;

for (i = ( n % 2 ? 3:2); i <= n; i += 2)

y *= i;

//Печать y

getch();

return 0;

}

Пример 4. Распечатать цифры целого числа a в обратном порядке.

В разделе «Цикл с предусловием», пример 5, рассматривался алгоритм решения задачи с использованием цикла while. При использовании цикла for в качестве параметра цикла используется переменная a1. Инициализация параметра производится в заголовке цикла (a1=abs(a)). Изменение его значения производится здесь же (a1/=10). Проверка условия продолжения цикла (a != 0) в программе проводится, с учетом рассматриваемого выше правила, что false – значение равное 0, а любое значение, отличное от 0, рассматривается, как true.

Блок-схема.

Си-программа

int main()

{

int a,d,a1;

//Ввод значения а

for ( a1=abs(a); a1; a1 /= 10)

{

d = a1 % 10;

printf(" %1d", d);

}

getch();

return 0;

}

Пример 5. Вычислить ряд

четырьмя способами:

1) последовательно слева направо;

2) последовательно справа налево;

3) слева направо отдельно положительные и отрицательные элементы, затем их вычитание;

4) то же, но справа налево.

int main()

{

float

SumLR, // сумма, все слева направо

SumRL, // сумма, все справа налево

SumLRNeg, // сумма, отрицательные эл-ты, слева направо

SumLRPos, // сумма, положительные эл-ты, слева направо

SumRLNeg, // сумма, отрицательные эл-ты, справа налево

SumRLPos, // сумма, положительные эл-ты, справа налево

ElemLRNeg, // очередной отриц.элемент, слева направо

ElemLRPos, // очередной полож.элемент, слева направо

ElemRLNeg, // очередной отриц.элемент, справа налево

ElemRLPos; // очередной полож.элемент, справа налево

int i; // параметр цикла

SumLR=0; SumRL=0; SumLRPos=0; SumLRNeg=0;

SumRLPos=0; SumRLNeg=0;

for (i=1;i<=5000;i++)

{

ElemLRPos=1./(2*i-1); ElemLRNeg=1./(2*i);

ElemRLPos=1./(10001-2*i); ElemRLNeg=1./(10002-2*i);

SumLR=SumLR+ElemLRPos-ElemLRNeg;

SumRL=SumRL+ElemRLPos-ElemRLNeg;

SumLRNeg=SumLRNeg+ElemLRNeg;

SumLRPos=SumLRPos+ElemLRPos;

SumRLNeg=SumRLNeg+ElemRLNeg;

SumRLPos=SumRLPos+ElemRLPos;

}

printf("SumLR = %10.7f\n",SumLR);

printf("SumRL = %10.7f\n",SumRL);

printf("SumLRPos-SumLRNeg = %10.7f\n",SumLRPos-SumLRNeg);

printf("SumRLPos-SumRLNeg = %10.7f\n",SumRLPos-SumRLNeg);

getch();

return 0;

}

Примечание. Для описания вещественных переменных использован тип floate вместо типа double, чтобы более четко показать влияние погрешности представления вещественных значений на результат вычислений (в типе floate мантисса имеет длину 23 бита вместо 52, как в типе double, что определяет меньшее количество значащих цифр в представлении вещественного числа).

Программа печатает следующие результаты:

SumLR 0.6930732

SumRL 0.6930972

SumLRPos-SumLRNeg 0.6931019

SumRLPos-SumRLNeg 0.6930976

Наиболее точная здесь сумма - SumRL.

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

Рассмотрим, как изменяется число 1/9000 при сложении его с числом 1/8998 или с числом 1/3. Мантиссу будем представлять с семью десятичными цифрами.

1/9000 = 0,0001111111 = 0,1111111 

1/8998 = 0,0001111357 = 0,1111357 

1/3 = 0,3333333 = 0,3333333 

a) 0, 1 1 1 1 1 1 1 

+ 0, 1 1 1 1 3 5 7 



0, 2 2 2 2 4 6 8 

б) 0, 1 1 1 1 1 1 1  0, 0 0 0 1 1 1 1 

+ 0, 3 3 3 3 3 3 3   + 0, 3 3 3 3 3 3 3 

 

0, 3 3 3 4 4 4 4 

Во втором случае в первом слагаемом потеряны три значащие цифры.

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

П Р О Г Р А М М И Р О В А Н И Е И Т Е Р А Ц И О Н Н Ы Х Ц И К Л О В

Итерационным называется такой вычислительный процесс, для которого заранее неизвестно количество выполнений цикла; это количество зависит от конкретных значений исходных данных. Критерием окончания циклического процесса является выполнение некоторого заранее заданного условия. Алгоритмы и программы, приведенные в разделах, где рассматриваются циклы while и do-while, являются примерами итерационных циклов.

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

Как правило, признаком окончания вычислений является достижение заданной точности:

где - максимально допустимая погрешность вычисления функции ;

- предыдущее и очередное значения функции .

Пример 1. Итерационная формула Ньютона для функции :

,

где - начальное приближение. В частности, для получим:

.

Рассмотрим действие формулы Ньютона для = 16; = 0.01 .

= 16 ;

= 0.5 * (16 + 16/16) = 8.5 ;

= 0.5 * (8.5 + 16/8.5) = 5.1911764 ;

= 0.5 * (5.191176 + 16/5.191176) = 4.1366649 ;

= 0.5 * (4.136664 + 16/4.136664) = 4.0022573 ;

= 0.5 * (4.002257 + 16/4.002257) = 4.000005 .

Условие выполняется при n = 4. Следовательно, значение = 4.000005 есть значение искомой функции при = 16 с погрешностью не более = 0.01 .

а) б)

В блок-схеме варианта а) нельзя применить оператор цикла с предусловием, так как в операторе while проверка условия выполнения цикла должна производиться в самом начале цикла. В связи с этим внесены изменения в схему реализации итерационной формулы Ньютона (вариант б)).

В программе вместо исходных переменных будут использованы переменные y, yn.

int main()

{

float const eps = 0.00001;

float x,y,yn;

//Ввод и печать x

y=x; yn=x+2*eps;

if (x>0)

while (fabs(y-yn)>eps)

{

yn=y;

y=0.5*(yn+x/yn);

}

//Печать y

getch();

return 0;

}

Примечания.

1.Функция fabs() возвращает модуль выражения типа float. Для использования этой функции подключается заголовочный файл "math.h".

2.Оператор "if (x>0)" в программе исключает прерывание программы из-за деления на нуль при x = 0 (если x = 0, то при первом выполнении цикла yn = y = 0).

3.Оператор "yn=x+2*eps;" в программе введен исключительно для задания значения переменной yn, которая используется в проверке условия входа в цикл. От него можно избавиться, если использовать цикл do-while. Ниже приведена реализация этого алгоритма с циклом do-while.

int main()

{

float const eps = 0.01;

float x,y,yn;

//Ввод и печать x

x=16;

y=x;

if (x>0)

do

{

yn=y;

y=0.5*(yn+x/yn);

}

while (fabs(y-yn)>eps);

//Печать y

getch();

return 0;

}

Пример 2. Вычисление функции по ее разложению в ряд.

В Си, как и в других языках программирования высокого уровня, определены стандартные функции sin(x), cos(x) и др. Вполне очевидно, что вычисление этих функций производится по некоторым формулам, а не по таблицам значений. Одним из методов вычисления функций является разложение их в ряд, в частности, в так называемый ряд Маклорена (существуют и другие ряды, например, ряд Лорана, ряд Фурье, разложение по полиномам Чебышева и пр.). Программная реализация таких рядов сводится к организации итерационных циклов.

Рассмотрим методику вычисления по формулам разложения в ряд на примере функции .

( 1 )

Как известно, n! = 1  2  3  ...  n . Например, 5! = 1  2  3  4  5 = 120.

В частности, считают 1! = 1; 0! = 1 .

Тогда

Является очевидным, что при малых значениях аргумента (например, < 0.1), значение функции примерно равно первому члену ее разложения в ряд, т.е. значению самого аргумента .

Пусть . Тогда

0.5-0.020833334+0.00026041688-

- 0.0000015500993+...

При = 0.001 достаточно взять первые три члена разложения функции sin(x) в ряд. Тогда y = sin(0.5) = 0.47942552 .

Как видно из примера, элементы разложения функции очень быстро убывают по абсолютному значению, стремясь в бесконечности к нулю.

Обозначим члены ряда через . Тогда общий член ряда

( 2 )

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

…………………………..

( 3 )

Условие окончания вычислений (окончания итерационного цикла):

или, используя (3),

.

Каждый очередной член ряда (1) можно вычислять непосредственно по формуле (2), задав соответствующее значение . Однако при этом значительная часть вычислений будет дублировать друг друга.

Например, при вычислении нужно выполнить действия

,

для вычисления - действия

.

Однако при вычислении уже были получены значения и 5! Поэтому при вычислении целесообразно использовать уже имеющееся значение :

Выведем формулу для общего случая вычисления .

Из формулы (2), подставив вместо значение , получим

.

Тогда

( 4 )

Формула типа (4), в которой для вычисления очередного члена числовой последовательности используется значение ее предыдущего члена , называется рекуррентной.

Для вычисления ряда (1) имеем следующие рабочие формулы:

Блок-схема вычислений:

Значение целочисленной переменной непосредственно входит в формулу для вычисления элемента . Чтобы исключить многочисленные преобразования intfloat, в программе переменная объявлена вещественной. Функция pow(x,2) возвращает значение x2, она описана в заголовочном файле"math.h".

Си-программа.

int main()

{

float const eps = 0.00001;

float x,a,S,n;

//Ввод и печать x

x=0.5;

a=x; S=x; n=0.;

while (fabs(a)>eps)

{

a=-pow(x,2)*a/((2*n+2)*(2*n+3));

S+=a; n+=1;

}

//Печать S

getch();

return 0;

}

М А С С И В Ы

Массив - это группа элементов, названная одним именем. Элементы массива обозначают именем массива, их местоположение в массиве определяется приписываемым к имени массива индексом.

Рассмотрим систему линейных алгебраических уравнений -го порядка:

В матричном виде эту систему можно записать следующим образом:

,

где - матрица коэффициентов, и - соответственно вектор свободных членов и вектор неизвестных:

- это массивы. В массивах и положение элемента определяется одним индексом. Это одномерные массивы. В матрице положение элемента определяется двумя индексами (номер строки и номер столбца ). Это двумерный массив. В принципе возможны трехмерные, четырехмерные и т.д. массивы, однако в реальных задачах наиболее часто используются одно- и двумерные массивы, очень редко - трехмерные массивы.

Решение рассматриваемой системы уравнений можно представить в виде

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

В Си-программе массивы относятся к структурированным типам. Массив состоит из фиксированного количества компонентов одного и того же типа. Компонент определяется именем массива и индексом. Индекс может вычисляться, поэтому он должен определяться целочисленным типом. В Си элементы массива всегда нумеруются, начиная с нуля. Следовательно, при описании массива необходимо указывать тип компонента и количество элементов.

Описание одномерного массива:

#define NMAX 1000

int main()

{

int a[10], b[50];

double x[NMAX];

В данном фрагменте определены:

- массив a из 10 элементов типа int с индексами a[0], a[1], … a[9]

- массив b из 50 элементов типа int с индексами b[0], b[1], … b[49]

- массив x из 1000 элементов типа double с индексами x[0],x[1], … x[999]. Количество элементов массива x задается константой NMAX, определенной в начале программы директивой #define.

Соседние файлы в папке Прогр_обменка