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

Программирование 1

.pdf
Скачиваний:
35
Добавлен:
20.03.2016
Размер:
1.04 Mб
Скачать

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

1.Вычисляется выражение.

2.Если выражение ложно, то выполнение оператора while заканчивается и выполняется следующий по порядку оператор. Если выражение истинно, то выполняется тело оператора while.

3.Процесс повторяется с пункта 1.

Оператор цикла вида

for (выражение-1; выражение-2; выражение-3) тело ;

может быть заменен оператором while следующим образом:

выражение-1;

while (выражение-2) { тело

выражение-3;

}

Так же как и при выполнении оператора for, в операторе while вначале происходит проверка условия. Поэтому оператор while удобно использовать в ситуациях, когда тело оператора не всегда нужно выполнять.

Внутри операторов for и while можно использовать локальные переменные, которые должны быть объявлены с определением соответствующих типов.

5.4 Оператор do while

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

do тело while (выражение);

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

1.Выполняется тело цикла (которое может быть составным оператором).

2.Вычисляется выражение.

3.Если выражение ложно, то выполнение оператора do while заканчивается

ивыполняется следующий по порядку оператор. Если выражение истинно, то выполнение оператора продолжается с пункта 1.

Чтобы прервать выполнение цикла до того, как условие станет ложным, можно использовать оператор break.

Операторы while и do while могут быть вложенными. Пример:

int i,j,k;

...

i=0; j=0; k=0; do { i++;

41

j--;

while (a[k] < i) k++;

}

while (i<30 && j<-30);

5.5 Оператор continue

Оператор continue, как и оператор break, используется только внутри операторов цикла, но в отличие от него выполнение программы продолжается не с оператора, следующего за прерванным оператором, а с начала прерванного оператора. Формат оператора следующий: continue;

Пример:

int main() { int a,b;

for (a=1,b=0; a<100; b+=a,a++) { if (b%2) continue;

... /* обработка четных сумм */

}

return 0;

}

Когда сумма чисел от 1 до а становится нечетной, оператор continue передает управление на очередную итерацию цикла for, не выполняя операторы обработки четных сумм.

42

6Регулярные типы данных. Массивы

6.1Понятие регулярного типа

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

A[5] S[k+1] B[3,5].

Пример описания массивов. float A[20],B[20];

int C[30];

В данном примере описаны одномерные массивы с именами A, B, C, причём массивы A и B имеют элементы типа float, порядковые индексы элементов изменяются от 0 до 19, а массив С – с элементами типа int, и индексами от 0 до 29. В квадратных скобках указывается количество элементов в массиве, нумерация которого всегда с 0 до N-1, где N – количество элементов массива.

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

Например, int a[10]; определяет массив из 10 элементов а[0], а[1], ..., а[9]. float Z[13][6]; определяет двумерный массив, первый индекс которого принимает 13 значений от 0 до 12, второй индекс принимает 6 значений от 0 до 5. Таким образом, элементы двумерного массива Z можно перечислить так:

Z[0][0], Z[0][l], Z[0][2],...,Z[12][4], Z[12][5]

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

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

43

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

Пример:

Вычисление среднего и дисперсии. Введя значение n из диапазона (0<n<=100) и значения n первых элементов массива х[0], х[1],...,х[n-1], вычислить среднее и оценку дисперсии значений введенных элементов массива. Задачу решает следующая программа:

/* Вычисление среднего и дисперсии */

#include <stdio.h> void main ( )

{

/*n - количество элементов */

int i,j,n;

/*b-среднее,d-оценка дисперсии; */

float a,b,d,x[100],e; /*а,е-вспомогательные*/ while (1) {

printf("\n Введите значение n="); scanf("%d", &n);

if( n > 0 && n <= 100 ) break;

printf("\n Ошибка! Необходимо 0<n<101 "); }

/* Конец цикла ввода Значения n */ printf("\n Введите значения элементов: \n"); for( b=0.0,i=0; i<n; i++) {

printf("x[%d] = ", i); scanf("%f", &x[i]);

b+=x[i];/* Вычисление суммы элементов */ } b/=n;/* Вычисление среднего */ for(j=0,d=0.0; j<n; j++) {

a=x[j]-b; d+=a*a;

} /* Оценка дисперсии*/ d/=n;

printf("\n Среднее =%f, дисперсия=%f",b,d);

}

В программе определен массив х со 100 элементами, хотя в каждом конкретном случае используются только первые n из них. Ввод значения n сопровождается проверкой допустимости вводимого значения. В качестве условия после while записано заведомо истинное выражение 1, поэтому выход из цикла (оператор break) возможен только при вводе правильного значения n, удовлетворяющего неравенству 0<n<101. Следующий цикл обеспечивает ввод n элементов массива и получение их суммы (b). Затем в цикле вычисляется сумма d

44

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

Введите значение n=5 Введите значение элементов:

х[0]=4 х[1]=5 х[2]=6 х[3]=7 х[4]=8

Среднее=6.000000, дисперсия=2.000000

 

10

 

5

 

 

5

 

 

Найти сумму S

 

a

ij

 

 

a

.

 

 

 

 

ij

 

j 1

i 1

 

 

i 1

 

 

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

double a[10][5]; for(s=0.0, j=0; j<10; j++)

{

for( p=1.0,c=0.0, i=0; i<5; i++)

{

p*=a[j][i]; c+=a[j][i];

}

s+=c+p;

}

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

Для

 

иллюстрации

рассмотрим

фрагмент

другой

программы

для вычисления

суммы

произведений

элементов

строк

той же

матрицы:

 

 

 

 

 

 

 

 

 

10

5

 

 

 

 

 

 

S aij

.

 

 

 

 

 

 

j 1

i 1

 

 

 

 

 

 

double

a[10][5];

 

 

 

 

 

for

(j=0,s=0.0;

j<10;

j++)

 

 

 

 

{

 

 

 

 

 

 

for

(i=0,p=1.0;

i<5; i++)

 

 

 

{

 

 

 

 

 

 

 

 

if (a[j][i] == 0.0) break;

 

 

 

p*=a[j][i];

 

 

 

 

 

45

}

if (i <5) continue; s+=p;

}

Внутренний цикл по i прерывается, если обнаруживается нулевой элемент матрицы. В этом случае произведение элементов столбца заведомо равно нулю, и его не нужно вычислять. Во внешнем цикле проверяется значение i. Если i<5, т.е. элемент a[j][i] оказался нулевым, то оператор continue передает управление на ближайший оператор цикла, и, таким образом, не происходит увеличение s на величину "недосчитанного" значения р. Если внутренний цикл завершен естественно, то i равно 5, и оператор continue не может выполняться.

6.2 Алгоритмы сортировки массивов

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

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

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

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

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

6.3 Постановка задачи сортировки и методы её решения

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

46

последовательность строк текста. Необходимо только определить какой из элементов пары «больше» другого.

Более важным для выбора алгоритма является местоположение данных – в оперативной памяти компьютера или на внешнем устройстве.

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

Различают два вида сортировки данных:

сортировка данных, расположенных в оперативной памяти компьютера (внутренняя сортировка);

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

Сформулируем постановку задачи сортировки данных для внутренней сортировки одномерного числового массива по возрастанию.

Имеется одномерный массив чисел, состоящий из n элементов: X[n]. Переставить элементы массива так, чтобы их значения располагались в порядке возрастания. Другими словами, для любой пары элементов X[i] и X[i+1] выполняется неравенство вида:

X[i] <= X[i+1].

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

При разработке программы можно воспользоваться различными алгоритмами. Наиболее известными являются следующие:

метод сортировки обменами («пузырьковая» сортировка);

метод сортировки вставками;

метод сортировки выбором элемента;

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

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

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

Необходимо, введя значение переменной 1<n<=100 и значения n первых элементов массива а[0],а[1],...,а[n-1], упорядочить эти первые элементы массива по возрастанию их значений.

47

6.4 Алгоритм прямого упорядочения (Алгоритм сортировки выбором элемента)

В программе реализован алгоритм прямого упорядочения – каждый элемент a[i], начиная с а[0] и кончая а[n-2], сравнивается со всеми последующими, и на место a[i] выбирается минимальный. Таким образом, а[0] принимает минимальное значение, а[1] - минимальное из оставшихся и т.д. Недостаток этого алгоритма состоит в том, что в нем фиксированное число сравнений, не зависимое от исходного расположения значений элементов. Даже для уже упорядоченного массива придется выполнить то же самое количество итераций (n-1)*n/2, так как условия окончания циклов не связаны со свойствами, т.е. с размещением элементов массива.

Текст программы:

/* Упорядочение элементов массива */

#include <stdio.h> main()

{

int n,i,j;

double a[100],b; while(1)

{

printf("\n Введите количество элементов n=") ; scanf("%d",&n);

if (n > 1 && n <= 100) break; printf("Ошибка! Необходимо 1<n<=100!");

}

printf("\n Введите значения элементов массива:\n"); for(j=0; j<n; j++)

{

printf("a[%d]=", j+1); scanf<"%lf",&a[j]);

}

for(i=0; i<n-1; i++) for(j=i+1; j<n; j++) if(a[i]>a[j])

{

b=a[i] ; a[i]=a[j] ; a[j]=b;

}

printf("\nУпорядоченный массив: \n"); for(j=0/ j<n; j++)

printf ("a[%d]=%f\n",j +l,a[j]) ;

48

}

Результаты выполнения программы: Введите количество элементов n= -15 Ошибка! Необходимо 1<n<=100! Введите количество элементов n=3 Введите значения элементов массива:

а[1]

=

88.8

а[2]

=

-3.3

а[3]

=

0.11

Упорядоченный массив:

а[1]

=

-3.3

а[2]

= 0.11

а[3]

=

88.8

Обратите внимание, что при заполнении массива и при печати результатов его упорядочения индексация элементов выполнена от 1 до n, как это обычно принято в математике. В программе на Си это соответствует изменению индекса от 0 до (n-1).

6.5 Алгоритм попарного сравнения соседних элементов («пузырьковая» сортировка)

Алгоритм попарного сравнения соседних элементов позволяет в ряде случаев уменьшить количество итераций при упорядочении. В цикле от 0 до n-2 каждый элемент a[i] массива сравнивается с последующим a[i+l] (0<i<n-l). Если a[i]>a[i+l], то значения этих элементов меняются местами. Упорядочение заканчивается, если оказалось, что a[i] не больше a[i+l] для всех i. Пусть k - количество перестановок при очередном просмотре. Тогда упорядочение можно осуществить с помощью такой последовательности операторов:

do {

 

for

(i=0,k=0; i<n-1; i++)

if (

a[i] > a[i+1] )

{

 

b=a[i];

a[i]=a[i+1]; a[i+1]=b; k=k+1;

}

n-- ;

} while( k > 0 ) ;

Здесь количество повторений внешнего цикла зависит от исходного расположения значений элементов массива. После первого завершения внутреннего цикла элемент а[n-1] становится максимальным. После второго окончания внутреннего цикла на место а[n-2] выбирается максимальный из оставшихся элементов и т.д. Таким образом, после j-ro выполнения внутреннего цикла элементы a[n-j],...,a[n-l] уже упорядочены, и следующий внутренний цикл

49

достаточно выполнить только для 0<i<(n-j-l). Именно поэтому после каждого окончания внутреннего цикла значение n уменьшается на 1.

Вслучае упорядоченности исходного массива внешний цикл повторяется только один раз, при этом выполняется (n-1) сравнений, k остается равным 0. Для случая, когда исходный массив упорядочен по убыванию, количество итераций внешнего цикла равно (n-1), а внутренний цикл последовательно выполняется (n- 1)*n/2 раз.

6.6Алгоритм сортировки выбором элемента

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

По сравнению с алгоритмами вставки и «пузырька» он в большинстве случаев может оказаться более быстрым.

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

void vibor(float *a[],int n)

{

int r,i,j; float y; for(i=1;i<=n-1;i++)

{

r=i;

for(j=i+1;j<=n;j++) if(a[r]>a[j]) r=j; y=a[r];a[r]=a[i];a[i]=y;

}

}

6.7Методы доступа к элементам массивов, использование указателей

Вязыке СИ под указателями понимают объекты, значением которых являются адреса других объектов памяти (переменных, массивов, функций и т.д.).

Вобщем случае указатель объявляется следующим образом:

int *p;

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

int a=10; p=&a;

Знак & определяет операцию взятия адреса. После такого присвоения указатель p будет содержать адрес переменной a, т.е. указывать на переменную.

50