- •Технология программирования вложенных циклов
- •Правила работы с вложенными циклами:
- •Выбор метода решения
- •Составление алгоритма решения
- •Программирование задачи
- •Описание многомерных массивов
- •Обозначение элементов многомерного массива
- •Создание двумерного массива
- •Постановка задачи
- •Формирование математической модели
- •Выбор метода решения
- •Составление алгоритма решения
- •Программирование задачи
- •Параллельно вложенные циклы
- •Постановка задачи
- •Формирование математической модели
- •Выбор метода решения
- •Составление алгоритма решения
- •Программирование задачи
- •Программирование задачи с графическим интерфейсом
- •Сортировка элементов массивов
- •Ранжирование числовых массивов
- •Ранжирование по убыванию основным методом
- •Постановка задачи
- •Формирование математической модели
- •Выбор метода решения
- •Составление алгоритма решения
- •Программирование задачи
- •Ранжирование по возрастанию методом «пузырька»
- •Постановка задачи
- •Формирование математической модели
- •Выбор метода решения
- •Составление алгоритма решения
- •Программирование задачи
- •Ранжирование символьных массивов
- •Ранжирование символов в строке по алфавиту
- •Постановка задачи
- •Формирование математической модели
- •Выбор метода решения
- •Составление алгоритма решения
- •Программирование задачи
- •Ранжирование массива символьных строк по алфавиту
- •Постановка задачи
- •Формирование математической модели
- •Выбор метода решения
- •Составление алгоритма решения
- •Программирование задачи
- •Заключение
- •Вопросы для контроля
-
Обозначение элементов многомерного массива
Описание многомерного массива позволяет использовать в программе любой из его элементов как индексированную переменную.
Индексированная переменная (индексное выражение) – обозначение ячейки для хранения конкретного элемента массива указанием идентификатора массива и индексов элемента по каждому измерению.
В массивах Си/Си++ индексы элементов на единицу меньше заданных математически. Это обстоятельство должно учитываться в программе, особенно при формировании условия повторения (выхода из) цикла.
Особенность работы с массивами в Си/Си++ – любой двумерный массив можно представить в виде одномерного при условии укрупнения единицы хранения (элемента).
Например, если в качестве элементов выбрать строки (столбцы), то двумерный массив превратится в одномерный массив строк (столбцов).
Хранение двумерного массива, например
X(m
n),
реализуется схемой распределения
оперативной памяти (рис. 9.4).
Для хранения трехмерного массива,
например S(k
m
n),
схема распределения оперативной
памяти представлена на рис 9.5 (первая
и последняя страницы).
-
x00
x01
.
.
.
x0j
.
.
.
x0 n-1
-
x10
x11
.
.
.
x1j
.
.
.
x1 n-1
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
xi0
xi1
.
.
.
xij
.
.
.
xi n-1
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
xm-1 0
x m-1 1
.
.
.
x m-1 j
.
.
.
x m-1 n-1
Рис. 9.4. Хранение элементов двумерного массива
-
s000
s001
.
.
.
s00j
.
.
.
s00 n-1
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
s0i0
s0i1
.
.
.
s0ij
.
.
.
s0i n-1
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
s0m-1 0
s0 m-1 1
.
.
.
s0 m-1 j
.
.
.
s0 m-1 n-1
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
sk-100
sk-101
.
.
.
sk-10j
.
.
.
sk-10 n-1
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
sk-1i0
sk-1i1
.
.
.
sk-1ij
.
.
.
sk-1i n-1
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
sk-1m-10
sk-1 m-1 1
.
.
.
sk-1 m-1 j
.
.
.
sk-1 m-1 n-1
Рис. 9.5. Хранение элементов трехмерного массива
Анализ схем показывает, что все элементы многомерного массива располагаются в оперативной памяти линейно (непрерывно и последовательно). При этом двумерный массив представляется последовательностью строк, трехмерный – последовательностью страниц, каждая из которых в свою очередь является последовательностью строк.
Изменение индексов происходит последовательно – справа налево. Например, для трёхмерного массива вначале полностью перебирается крайний правый индекс (столбцов), затем средний (строк) и последним – левый (страниц).
Длина ячейки хранения каждого элемента определяется типом массива.
Таким образом, любой трехмерный массив представляется в виде одномерного массива двумерных матриц (страниц), каждая из которых, в свою очередь, рассматривается как одномерный массив строк (столбцов).
Следовательно, n-мерный массив в Си/Си++ интерпретируется как совокупность массивов (n-1) размерности, которые также могут быть представлены совокупностью массивов еще меньшей размерности.
Структура обозначения индексированной переменной многомерного массива:
имя[индекс_1][индекс_2]...[индекс_i]...[индекс_n]
где имя – идентификатор массива;
индекс_i – целая константа, задающая номер элемента по i-му измерению;
[ ] – ограничители индекса элемента по каждому измерению.
Так, в описанном ранее массиве D(20
30)
элемент, расположенный в первом столбце
первой строки, обозначается индексным
выражением d[0][0], во втором
столбце той же строки – d[0][1],
в первом столбце второй строки – d[1][0],
текущий – d[i][j],
элемент последнего столбца, последней
строки – d[19][29].
Рассмотренный пример идентификации элементов массива D применим к любому из двумерных массивов, указанных в соответствующем описателе.
Для трехмерных массивов обозначение
элементов выполняется аналогично.
Например, в массиве S(10
5
15)
(описан ранее) элемент первой страницы
на пересечении первой строки и первого
столбца обозначается индексным выражением
s[0][0][0], элемент второго
столбца первой строки той же страницы
– s[0][0][1], второго столбца
второй строки первой страницы –
s[0][1][1], текущий – s[k][i][j],
а элемент последнего столбца, последней
строки, последней страницы – s[9][4][14].
Индекс, при необходимости, может задаваться арифметическим выражением. Например, d[i+2][5], d[i][j-1], s[3][i-1][j+2], s[k-3][i][2*j].
-
Внимание! Индекс на момент использования переменной должен быть определен (рассчитан) и укладываться в заданный описателем диапазон.
Рассмотренные формы представления индексированных переменных позволяют осуществить программную реализацию элементов алгоритма с использованием многомерных массивов.
Непрерывное и последовательное расположение элементов многомерного массива в оперативной памяти позволяет адрес каждого элемента представить зависимостью:
а = а1 + смещение,
где а – адрес некоторого текущего элемента массива;
а1 – адрес первого элемента массива;
смещение – номер текущего элемента относительно первого.
Смещение рассчитывается для массивов различной размерности по аналогичным методикам.
Так для двумерного массива
смещение = индекс_1*разм_2+индекс_2
т.е. произведение номера текущей строки на ее размер (число столбцов) плюс номер текущего столбца. Первое слагаемое определяет число элементов в вышерасположенных строках, второе – число элементов в текущей строке от ее начала до искомого включительно.
Для трехмерного массива
смещение = индекс_1*(разм_2* разм_3) +
+ индекс_2 * разм_3 + индекс_3
Первое слагаемое определяет число элементов в ранее расположенных страницах, второе – в предыдущих строках текущей страницы, третье – число элементов в текущей строке текущей страницы.
-
Внимание! Для любого массива размер первого измерения (разм_1) в расчете смещения не используется.
В качестве сомножителей (разм_i) используются значения, указанные в описателях массивов.
Описатели массивов определяют максимально возможные значения (размеры) каждого измерения. Это позволяет использовать зарезервированное пространство оперативной памяти полностью, либо частично, обеспечивая универсальность размеров в сторону уменьшения. Для двумерного массива это утверждение поясняется схемой (рис. 9.6):
|
Размер, заданный в описателе (максимальное число столбцов nmax) |
|
||||||||||||
|
Размер, используемый в расчетах (n) |
|
|
|||||||||||
|
|
|
|
|
|
|
|
|
|
|
Размер, использу-емый в расчетах (m) |
Размер, заданный в описателе (максимальное число строк mmax) |
||
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
||||
Рис. 9.6. Соответствие реальных размеров описанным
Размеры, указанные в описателе определяют количество зарезервированных в оперативной памяти ячеек. При этом ячейки создаваемого двумерного массива располагаются последовательно и линейно (построчно). Если в расчетах зарезервированное пространство используется частично (с меньшим числом строк и/или столбцов), то участки с хранимыми значениями будут чередоваться с неиспользуемыми, количество которых должно быть учтено при указании длины каждой строки в индексном выражении. Суммарное количество элементов каждой строки задано в описателе массива. Поэтому адреса любой ячейки определяется индексным выражением, использующим в качестве одного из параметров указанный в описателе размер.
Так, если двумерный массив z описан как z[10][20], а в задаче используется с размерами m=7, n=12, то адрес текущего элемента &z[i][j] = z[0] + i * 20 + j, а не &z[i][j] = z[0] + i * n + j.
Исходя из изложенного, адрес i-го, j-го элемента массива D(20х30) вычисляется по формуле
&(d[i][j]) = d[0] + i * 30 + j,
а адрес k-го, i-го, j-го элемента массива S(10х5х15) вычисляется как
&(s[k][i][j]) = s[0][0] + k * (5 * 15) + i * 15 + j
Таким образом, индексное выражение полностью определяет адрес конкретной ячейки для хранения соответствующего элемента через параметры описателя, а не укороченные, реально заданные.
С учетом изложенного идентификация переменных алгоритма и создаваемой программы представлена в табл. 9.1.
Таблица 9.1
|
Обозначение в алгоритме |
m |
n |
i |
j |
xпр |
xij |
yij |
|
Обозначение в программе |
m |
n |
i |
j |
xp |
x[i][j] |
y[i][j] |
На основании схемы алгоритма и таблицы идентификации составим программу решения задачи.
Классический вариант программирования задачи
#include <stdio.h> /* директивы */
#include <conio.h> /* препроцессора */
#include <windows.h>
#define M 10 /* увеличенные */
#define N 12 /* размеры массивов */
main( ) /* заголовок головной функции */
{
float x[M][N], y[M][N]; /* описатели массивов */
int i, j, n, m;
char buf[50]; /*описание символьного массива*/
clrscr( );
CharToOem(" Введите m (m<= ",buf); /* запрос */
printf("\n %s %d):",buf,M); /* и */
scanf("%d", &m); /* ввод */
CharToOem(" Введите n (n<= ",buf); /* фактических */
printf("\n %s %d):",buf,N); /* размеров */
scanf("%d", &n); /* массивов */
printf("\n n=%d m=%d ", n, m); /*вывод размеров массивов*/
for( i = 0; i < m; i++ ) /*заголовок внешнего цикла ввода x[i][j]*/
for( j = 0; j < n; j++ ) /*заголовок внутр. цикла ввода x[i][j]*/
{
CharToOem(" Введите значение ",buf); /* ввод */
printf("\n %s x[%d][%d]:",buf,i+1, j+1); /* элементов */
scanf("%f", & x[i][j]); /*массива Х*/
}
CharToOem(" Массив X",buf); /* вывод */
printf("\n %s \n",buf); /*заголовка*/
for(i = 0; i < m; i++)/* заголовок внешн. цикла вывода x[i][j]*/
{
for(j=0; j < n; j++)/*заголовок внутр. цикла вывода x[i][j]*/
printf(" %5.2f", x[i][j]);
printf("\n");
}
for(i = 0; i < m ; i++ /*заголовок внешн. цикла расчета y[i][j]*/
for( j = 0; j < n; j++)/*заголовок внутр. цикла расчета y[i][j]*/
y[ i ][ j ] = x[ i ][ j ] / 2.;
CharToOem(" Массив Y",buf); /* вывод */
printf("\n %s \n",buf); /*заголовка*/
for( i = 0 ; i < m ; i++ )/*заголовок внешн. цикла вывода y[i][j]*/
{
for(j = 0; j < n; j++) /*заголовок внутр. цикла вывода y[i][j]*/
printf(" %5.2f", y[i][j]);
printf("\n");
}
getch( );
}
2 3 – размеры массива;
10. 20. 30. – элементы первой строки;
100. 200. 300. – элементы второй строки.
Под закрывающей скобкой приведены исходные данные для решения задачи.
Результаты решения представлены в приложении 9.1.
Программирование задачи с графическим интерфейсом
Программирование задачи при использовании графического интерфейса предварим его разработкой.
ListBoxYi
ListBoxХi

Для ввода количества столбцов и строк массива планируем однострочные поля редактирования (EditN, EditМ). Для ввода элементов массива Х – многострочное поле редактирования (EditХ). Вывод элементов массивов X и Y реализуем в поля-списки (ListBoxXi, ListBoxYi).
Управление процессом решения реализуем двумя командными кнопками, расположенными в нижней части окна. Назначение каждой определяется ее названием.
Использование графического интерфейса для вывода числовых элементов двумерных массивов в виде таблицы требует:
-
представления каждого числового данного соответствующей символьной строкой;
-
формирования общей символьной строки (из полученных для каждого элемента строк), соответствующей числовой строке массива;
-
размещение сформированной общей символьной строки в окне вывода.
Представление числовых данных символьными строками комментариев не требует.
Формирование элементов выводимой строки в единое целое выполняется функцией «склеивания» строк strcat.
Функция «склеивания» символьных строк strcat( )
Функция предназначена для получения результирующей строки из двух исходных строк. Структура функции:
strcat( buf1, buf2 )
где strcat – обозначение функции;
buf1 – имя исходной (результирующей) символьной строки;
buf2 – имя добавляемой символьной строки;
( ) – ограничители аргумента.
Функция располагается в библиотеке string.h.
Правила записи и использования
-
Операнды buf1 и buf2 – символьные строки. Строка buf1 увеличивает свое значение после выполнения функции на величину buf2.
-
Обязательное условие формирования строк buf1 и buf2 – окончание каждой символом «\0».
-
Пробелы, при необходимости, формируются структурой соответствующей строки (включением в нее).
-
Однократное использование функции – чтение строки buf1, добавление к ней строки buf2 и занесение результата в buf1. Поэтому размер buf1 в описателе создается увеличенным (на величину добавляемых компонентов).
-
Многократное использование функции – последовательное добавление второго операнда (buf2) к предварительно полученной строке buf1.
-
Повторное использование функции для создания новой результирующей строки требует предварительной очистки первого аргумента функции. Один из вариантов – присваивание строке buf1 пустой строки: sprintf(buf1,"%s","");
-
Проверка результирующей строки на переполнение не выполняется.
-
Функция используется как операнд арифметического выражения (присваивания) или самостоятельный оператор.
Общий вид фрагмента программы «склеивания» символьных строк str и buf:
#include <string.h> /* директива препроцессора*/
…
char str[25], buf[10]; /*описатель символьных строк*/
…
EditStr->GetText(str, 10); /*ввод строки str из поля EditStr*/
EditBuf->GetText(buf, 10); /*ввод buf из поля EditBuf*/
strcat(str, buf); /*формирование результирующей строки str «склеиванием» исходных строк str и buf*/
Описатель типа определяет массивы str и buf как символьные максимальной длины 25 и 10 символов. Пятая и шестая строки предписывает ввод строк str и buf из полей EditStr и EditBuf соответственно. Оператор strcat(str, buf); формирует «склеенную» строку и хранит ее под именем str .
Многократное использование функции позволяет создать результирующую строку с любым количеством компонентов в пределах, предусмотренных размером buf1.
Вариант 1: последовательное соединение нескольких строк
#include <string.h> /* директива препроцессора*/
…
char str[25], buf1[10], buf2[5];/*описатель символьных строк*/
…
EditStr->GetText(str, 10); /*ввод строки str из поля EditStr*/
EditBuf1->GetText(buf1, 10); /*ввод buf1 из поля EditBuf1*/
EditBuf2->GetText(buf2, 10); /*ввод buf2 из поля EditBuf2*/
strcat(str, buf1); /*формирование результирующей строки str «склеиванием» исходных строк str и buf1*/
strcat(str, buf2); /*формирование результирующей строки str «склеиванием» полученных str и buf2*/
Описатель типа определяет массивы str, buf1 и buf2 как символьные, максимальной длины 25, 10 и 5 символов, соответственно. Пятая, шестая и седьмая строки предписывает ввод str, buf1 и buf2 из полей EditStr, EditBuf1 и EditBuf2 соответственно. Операторы strcat(str, buf1); и strcat(str, buf2); последовательно формируют «склеенную» строку из str, buf1 и buf2. Полученная строка имеет имя str .
Вариант 2: использование функции в теле цикла.
#include <string.h> /* директива препроцессора*/
…
char str[50] = “ ”, buf[10];/*описание и инициализация
символьных строк*/
…
for( j = 0 ; j < 5 ; j++ ) /* заголовок цикла ввода buf и формирования str*/
{
EditBuf->GetLine( buf, 10, j); /* ввод buf */
strcat(str, buf); /*формирование результирующей строки str «склеиванием» исходных строк str и buf*/
}
…
Описатель типа определяет массивы str и buf как символьные максимальной длины 50 и 10 символов соответственно и инициализирует str пустой строкой. Оператор EditBuf->GetLine (buf, 10, j); предписывает ввод buf из j-й строки многострочного поля EditBuf. Оператор strcat(str, buf); формирует в теле цикла, из последовательно вводимых строк buf, «склеенную» строку и хранит ее под именем str .
С учетом планируемого интерфейса выполним программирование задачи.
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
…
void TSumprDlgClient::Ok()
{
// INSERT>> Your code here.
float x[M][N], y[M][N]; /* описатели массивов */
int i, j, n, m;
char buf[30],buf1[90]=" "; /*описание символьного массива*/
ListBoxYi->ClearList(); /*очистка поля вывода*/
ListBoxXi->ClearList(); /*очистка поля вывода*/
EditN->GetText(buf, 10); /*ввод количества*/
n = atoi(buf); /* столбцов массива*/
EditM->GetText(buf, 10); /*ввод количества*/
m = atoi(buf); /* строк массива*/
for( i = 0 ; i < m ; i++ ) /* заголовок внешн. цикла ввода x[i][j] */
for( j = 0 ; j < n ; j++ ) /* заголовок внутр. цикла ввода x[i][j] */
{
EditX->GetLine(buf, 30, i*n+j); /* ввод элементов */
x[i][j]=atof(buf); /* массива Х*/
}
for(i = 0; i < m; i++) /*заголовок внешн. цикла вывода x[i][j]*/
{
for(j = 0; j < n; j++)/*заголовок внутр. цикла вывода x[i][j]*/
{
sprintf(buf,"%11.3f",x[i][j]); /* вывод текущих*/
strcat(buf1, buf); /*склеенных*/
}
ListBoxXi->AddString(buf1); /*значений xi*/
sprintf(buf1,"%s","");
}
for( i = 0; i < m; i++)/*заголовок внешн. цикла расчета y[i][j]*/
for(j = 0; j < n; j++) /*заголовок внутр. цикла расчета y[i][j]*/
y[ i ][ j ] = x[ i ][ j ] / 2.;
for( i = 0 ; i < m ; i++ )/*заголовок внешн. цикла вывода y[i][j]*/
{
for(j = 0; j < n; j++)/*заголовок внутр. цикла вывода y[i][j]*/
{
sprintf(buf,"%11.6f",y[i][j]); /* вывод текущих*/
strcat(buf1, buf); /*склеенных*/
}
ListBoxYi->AddString(buf1); /*значений yi*/
sprintf(buf1,"%s","");
}
}
3 2 – размеры массива;
10. 20. 30. – элементы первой строки;
100. 200. 300. – элементы второй строки.
Под закрывающей скобкой приведены исходные данные для решения задачи.
Результаты решения представлены в приложении 9.2.
