Вопросы для проверки знаний.

. . .

SetLength(a,0); SetLength(b,0); {Удаление динамических массивов a и b}

end. {конец тела внешней программы}

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

Пример 4. Разработать программу, в которой выполнялись бы следующие действия:

а) ввод с клавиатуры числа строк n и столбцов m целочисленной матрицы M[1..n,1..m],

б) ввод с клавиатуры элементов матрицы M[1..n,1..m],

в) формирование с помощью отдельной процедуры SUM_STRвектора S[1..n], состоящего из сумм элементов в строках матрицы,

г) вывод вектора S[1..n]на экран,

д) удаление всех динамических массивов программы.

Решение. Обозначим номера строк и столбцов матрицы через i и j, динамический линейный массив, соответствующий ей, через LM. Функция k = k (i, j, m)для расчета номеров коэффициентов LM по коэффициентам матрицы и числу столбцов взята из примера 5 п.8.5. Число столбцов матрицы m передается в процедуру суммирования элементов строк SUM_STR по двум причинам: для использования в функции k (i, j, m) и для расчета числа строк n (чтобы не передавать его через заголовок процедуры). При задании номеров элементов необходимо учитывать, что в матрице начало отсчета индексов с 1, а в динамических массивах – с 0.

var LM: array of integer;{ Описание дин. массиваLM}

S: array of integer;{ Описание дин. массиваS}

n,m,i,j,el: integer;

function k(i,j,m:integer):integer; {функция для расчета k = k (i, j, m) = (i-1)m +(j-1) }

begin k:= (i-1)*m + j -1; end;

procedure SUM_STR(L:array of integer; m:integer; var STR_S:array of integer);

{процедура для расчета вектора из сумм элементов в строках матрицы }

var k,n,i_first,i_fin,i,j:integer;

begin{начало тела процедуры SUM_STR }

i_first:=Low(L);i_fin:=High(L);{определение границ массиваL}

n:=(i_fin-i_first+1) div m;{определение количества строкn в матрице, соответствующей динамическому массиву L}

for i:=1 to n do STR_S[i-1]:=0;{начальная инициализация массива STR_S нулями}

for i:=1 to n do{расчет элементов массива STR_S}

for j:=1 to m do

begin k:=(i-1)*m+j-1; STR_S[i-1]:=STR_S[i-1]+L[k] end;

end; {конец тела процедуры SUM_STR }

begin{ начало тела основной программы}

writeln (' Vvedite сhislo strok:');read(n);{запрос и ввод числа строк n}

writeln (' Vvedite сhislo stolbtsov:');read(m);{запрос и ввод числа строк m}

SetLength (LM,n*m);{Создание динамического массиваLMдля ввода матрицыM[1..n,1..m] }

SetLength (S,n);{Создание динамического массиваSдля сумм элементов в столбцах матрицы }

for i:=1 to n do{ввод элементов матрицы с клавиатуры}

for j:=1 to m do begin

writeln('vvedite element M[',i,',',j,']=');{запрос на ввод элемента M[i,j]}

read(el); LM[k(i,j,m)]:=el;{ввод элемента M[i,j] в линейный массив LM }

end;

for i:=1 to n do begin{вывод матрицы M[1..n,1..n] на экран}

for j:=1 to m do write(' M[',i,',',j,']=',LM[k(i,j,m)]:6);{вывод строки}

writeln;{переход на очередную строку}

end;

SUM_STR(LM, m, S); {вызов процедуры SUM_STR }

for i:=0 to n-1 do{Печать динамического массиваS }

writeln(' Sum of elements in stroke ',i+1,'=',S[i]:6);

SetLength(LM,0); SetLength(S,0); //Удаление динамических массивов LM и S

end. {конец тела основной программы}

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

1. Как передают в подпрограммы статические массивы постоянного размера с использованием описания их типа в вызывающем блоке ?

2. Как передают в подпрограммы статические массивы ограниченного размера с использованием описания их типа в вызывающем блоке ?

3. Где и с какой целью применяют открытые массивы ?

4. Какие ограничения снимает использование открытых массивов в случае передачи в подпрограммы массивов различного размера ?

5. Какие ограничения снимает передача через открытые массивы динамических массивов по сравнению со статическими ?

6. Можно ли использовать открытые массивы для передачи многомерных динамических массивов переменных размеров ?

Практические задания.

1. Разработать варианты а) - г) программы, в которой производится ввод с клавиатуры двух векторов`a = (a1,...,an) и`b = (b1,...,bn) и последующий расчет их скалярного произведения (`a ,`b) = (a1b1 +...+anbn) с помощью отдельной функции Sc(`a ,`b ):

а) с использованием для векторов`a и`b статических массивов постоянной длины n=3 и опиcания типа массива в вызывающем блоке;

б) с использованием для векторов`a и`b статических массивов переменной длины 1 ≤ n ≤ 100 и опиcания типа массива в вызывающем блоке;

в) с использованием для векторов`a и`b статических массивов переменной длины 1 ≤ n ≤ 100 и открытых массивов в заголовке кода функции Sc(`a ,`b );

г) с использованием векторов`a и`b произвольной длины n, динамических массивов в вызывающем блоке и открытых массивов в заголовке кода функции Sc(`a ,`b ).


 

Пока же рассмотрим примеры решения типовых задач.

1. Заданы вектора A и B, содержащие по 5 элементов. Используя подпрограмму, найти их скалярное произведение по формуле

Поиск скалярного произведения реализуем в виде подпрограммы-функции scal.

type vector=array [1..5] of real;

 

function scal (n:integer;

var a,b:vector):real;

var i:integer;

s:real;

begin

s:=0;

for i:=1 to n do s:=s+a[i]*b[i];

scal:=s;

end;

 

var i:integer;

a,b:vector;

s:real;

begin

writeln ('Вектор 1 из 5 элементов:');

for i:=1 to 5 do read (a[i]);

writeln ('Вектор 2 из 5 элементов:');

for i:=1 to 5 do read (b[i]);

s:=scal(5,a,b);

writeln ('s=',s:10:3);

end.

2. Сформировать по введенному с клавиатуры вектору A размерности n вектор res, компонентами которого являются отклонения элементов A от их арифметического среднего (подобная задача уже решалась выше, расширим ее на случай вектора).

Задача предполагает написание, по меньшей мере, двух подпрограмм: функция Middle будет вычислять арифметическое среднее элементов вектора, а процедура Otkl - формировать по вектору A и ранее найденному среднему mid искомый вектор отклонений b. Компоненты вектора b при этом будут вычисляться по формуле . Поскольку о размерности векторов в задаче ничего не сказано, укажем в разделе type максимальную размерность, равную 100 элементам.

type vector= array [1..100] of real;

 

function Middle (n:integer;

var a:vector):real;

var j:integer;

res:real;

begin

res:=0.0;

for j:=1 to n do res:=res+a[j];

Middle:=res/n;

end;

 

procedure Otkl (n:integer; mid:real;

var a,b:vector);

var j:integer;

begin

for j:=1 to n do b[j]:=abs(a[j]-mid);

end;

 

var a,res: vector;

i,n:integer;

s:real;

begin

write ('Размерность? ');

readln (n);

for i:=1 to n do begin

write ('A[',i,']=');

readln (a[i]);

end;

s:=Middle (n,a);

Otkl (n,s,a,res);

for i:=1 to n do

writeln ('res[',i,']=',res[i]:8:2);

end.

3. Используя подпрограмму, написать и проверить программу перемножения двух матриц.

Как известно, матрица A размерностью n3m может быть умножена на матрицу B размерностью m3p по следующей формуле: где ci,j -- элемент получающейся в результате перемножения матрицы С размерностью n3m. Из формулы видно, что для умножения двух матриц нам понадобится тройной цикл: внешний цикл по i перебирает строки матрицы A, вложенный в него цикл по j выбирает в матрице B очередной столбец, а самый внутренний цикл по l умножает строку матрицы A на столбец матрицы B, получая элемент ci,j. Напишем соответствующую процедуру mmul и тестовую программу для нее:

type matrix=array[1..10,1..10] of real;

 

var a,b,c: matrix;

i,j,n,m,p: integer;

 

procedure mmul (n,m,k:integer;

var a,b,c:matrix);

var i,j,l:integer; s:real;

begin

for i:=1 to n do

for j:=1 to k do begin

s:=0;

for l:=1 to m do s:=s+a[i,l]*b[l,j];

c[i,j]:=s;

end;

end;

 

begin

repeat

writeln;

write ('Введите количество строк ',

'1 матрицы: ');

readln (n);

write ('Введите количество столбцов ',

'1 матрицы: ');

readln (m);

write ('Введите количество столбцов ',

'2 матрицы: ');

readln (p);

until (n>1) and (n<11) and (m>1)

and (m<11) and (p>1) and (p<11);

for i:=1 to n do begin

writeln ('Введите строку ',i,

' матрицы A из',m,'элементов:');

for j:=1 to m do read (a[i,j]);

end;

for i:=1 to m do begin

writeln ('Введите строку ',i,

' матрицы B из',p,'элементов:');

for j:=1 to p do read (b[i,j]);

end;

 

mmul (n,m,p,a,b,c);

for i:=1 to n do begin

writeln;

for j:=1 to p do write (c[i,j]:10:3);

end;

end.

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

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

Имеется N городов, между которыми налажены пассажирские перевозки. Между какими городами самый активный пассажиропоток?

Количество городов обозначим константой cities. После математической формализации задачи, нетрудно заметить, что перевозки из города i в город j могут быть занесены в элемент матрицы ai,j, таким образом, требуется определить величину max(ai,j+aj,i), учитывая перевозки "туда" и "обратно". Для поиска максимального пассажиропотока достаточно двойного цикла с переменной границей по счетчику вложенного цикла. Как и в других программах, выделим в отдельные подпрограммы также типовые задачи ввода и вывода матрицы, а также ввода вещественного значения с контролем допустимых числовых границ ввода.

const cities=10;

type matrix=array [1..cities,1..cities]

of integer;

 

function max1 (n:integer; var a:matrix;

var imax,jmax:integer):integer;

var i,j,m,p:integer;

begin

m:=a[1,2]; imax:=1; jmax:=2;

for i:=1 to n do

for j:=1 to n do

if (i<>j) then begin

p:=a[i,j]+a[j,i];

if p>m then begin

m:=p; imax:=i; jmax:=j;

end;

end;

max1:=p;

end;

 

function readNumber (s:string;

min,max:integer):integer;

var a:integer;

begin

repeat

write (s);

{$I-}readln(a);{$I+}

if IoResult<>0 then

writeln ('Ошибка, введено не число!')

else if (a<min) or (a>max) then

writeln ('Ошибка, введенное число ',

'принадлежит интервалу [',min, ',',

max, ']')

else break;

until false;

readNumber:=a;

end;

 

procedure readMatrix1 (var n:integer;

var a:matrix);

var i,j:integer; s,s1:string;

begin

n:=readNumber ('Введите число строк ',

'и столбцов матрицы: ',2,cities);

for i:=1 to n do

for j:=i+1 to n do begin

s:='A['; str(i,s1); s:=s+s1+',';

str(j,s1); s:=s+s1+']=';

a[i,j]:=readNumber (s,0,maxInt);

end;

end;

 

procedure writeMatrix1 (s:string;

n:integer; var a:matrix);

var i,j:integer;

begin

writeln (s);

for i:=1 to n do begin

for j:=1 to n do write (a[i,j]:7);

writeln;

end;

end;

 

var a:matrix;

n,gorod1,gorod2:integer;

 

begin

readMatrix1 (n,a);

max1 (n,a,gorod1,gorod2);

writeMatrix1 ('A=',n,a);

writeln ('Самый большой пассажиропоток ',

'между городами ',gorod1,' и ',gorod2);

readln;

end.