русс | укр

Мови програмуванняВідео уроки php mysqlПаскальСіАсемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование


Linux Unix Алгоритмічні мови Архітектура мікроконтролерів Введення в розробку розподілених інформаційних систем Дискретна математика Інформаційне обслуговування користувачів Інформація та моделювання в управлінні виробництвом Комп'ютерна графіка Лекції


Матриці й типові алгоритми обробки матриць


Дата додавання: 2014-11-28; переглядів: 1740.


Багато завдань пов'язані з обробкою багатомірних масивів даних. Найпоширеніші при цьому двовимірні масиви.

У математику двовимірні масиви представляються матрицею:

або, у скороченому записі,

, де

де n - число рядків матриці, m - число стовпців, індекси (номера) поточних рядка й стовпця, на перетинанні яких перебуває елемент .

Як і інші об'єкти даних, матриця описується в розділі var. Від вектора її опис відрізняється тим, що у квадратних дужках перелічуються два оператори діапазону, що вказують, відповідно, нумерацію рядків і стовпців матриці:

var Имяматрицы: array [n1..n2, m1..m2] of Тип;

Тут

n1..n2 – діапазон значень номера рядка, n1 і n2 – Цілочисельні константи;

m1..m2 – діапазон значень номера стовпця, значення m1 і m2 також Цілочисельні.

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

Під матрицю виділяється область пам'яті розмірністю n*m*k байт, де k – розмірність у байтах одного елемента. Для відомих нам типів даних цю розмірність можна довідатися з таблиці в розділі 2.2. В оперативній пам'яті матриця зберігається построчно.

Наприклад, для матриці A, описаної оператором виду

var A:array [1..5,1..4] of real;

виділяється 20 комірок пам'яті по 6 байт, причому в наступній за елементом A1,4 гнізду зберігається значення елемента A2,1.

Звертання до окремих елементів матриці здійснюється за допомогою змінної із двома індексами, наприклад:

ai,j a[i,j]

a2,1 a[2,1]

a2n,k a[2*n,k]

Перший індекс, як і в математику, завжди показує номер рядка, а другий – номер стовпця.

Оскільки адресація пам'яті в кожному разі линейна, слід розуміти матрицю як зручний для програміста структурний тип даних. В окремих випадках використання матриці може бути замінене використанням вектора з тим же кількістю елементів: так, матриці An,m завжди може бути зіставлений вектор b розмірністю n*m, а звертання до елемента A[i,j] при нумерації рядків і стовпців з одиниці може бути замінене на звертання до елемента b[(i-1)*m+j].

Подібно тому, як будь-яка послідовна обробка вектора виконується в циклі for, обробка матриці виконується в подвійному циклі for:

for i:=1 to n do

for j:=1 to m do

{Обробка елемента A[i,j]}

Згідно із правилом виконання кратних циклів, змінна j міняється в цьому подвійному циклі швидше, чим i, таким чином, обробка всіх елементів матриці буде виконана построчно. Для послідовної обробки всіх елементів матриці по стовпцях досить поміняти місцями цикли по i і j:

for j:=1 to m do

for i:=1 to n do

{Обробка елемента A[i,j]}

Теоретично ми могли б розв'язати цю же завдання й перестановкою індексів у звертанні до елемента матриці (A[j,i] замість A[i,j]), однак, щоб уникнути плутанини, робити цього не рекомендується.

Приведемо приклади використання подвійного циклу for для введення й виведення елементів матриці. Нехай матриця C розмірністю 4*2 ( як ми пам'ятаємо, це означає, що в матриці 4 рядка й 2 стовпця) описана оператором виду

var c:array [1..4,1..2] of real;

У програмі передбачене також 2 цілочисельних лічильника для рядків і стовпців матриці:

var i,j:integer;

У цьому випадку типове введення матриці із клавіатури міг би виглядати так:

writeln ('Уведіть матрицю C розмірністю 4*2');

for i:=1 to 4 do

for j:=1 to 2 do read (c[i,j]);

Іноді зручніше друкувати окреме запрошення до введення кожного елемента:

writeln ('Уведіть матрицю C розмірністю 4*2');

for i:=1 to 4 do

for j:=1 to 2 do begin

write ('C[',i,',',j,']=');

readln (c[i,j]);

end;

Наприклад, у якості запрошення до введення елемента C1,1 на екрані буде надруковано:

C[1,1]=

Оператор readln використовується, оскільки елементи вводяться по одному.

Як і для векторів, можливі альтернативні способи введення – опис матриці в розділі констант і формування її елементів з випадкових чисел. Приведемо приклад опису матриці констант розмірністю 2*3:

const d:array [1..2,1..3] of integer=(

(1,2,3),

(4,5,6)

);

Як видне із прикладу, для опису матриці констант створюється список її рядків, кожний рядок, у свою чергу, являє собою список значень елементів.

 

Вивід на екран матриці невеликої розмірності буває зручно реалізувати у вигляді "один рядок матриці на одному рядку екрана". Для цього елементи матриці у внутрішньому циклі друкуються оператором write, що не переводять рядок на екрані, а в зовнішньому циклі виконується writeln:

writeln ('Матриця C:');

for i:=1 to 4 do begin

writeln;

for j:=1 to 2 do write (c[i,j]:4:1);

end;

Зрозуміло, при виведення елементів матриці можна використовувати константи ширини й точності.

 

Базові алгоритми обробки матриць – ті ж, що ми вивчили в темах "Цикли" і "Одномірні масиви". Відмінність полягає в тому, що реалізацію цих алгоритмів можна умовно розглядати для двох типів завдань:

· Алгоритми реалізуються при обробці всіх елементів матриці.

· Алгоритми реалізуються усередині кожного рядка або кожного стовпця матриці.

У першому випадку завдання відрізняється від завдання з вектором лише тим, що використовуються подвійні цикли введення, обробки й виведення.

Пр. У матриці A розмірністю 4*4 знайти суму її позитивних елементів, добуток елементів, значення яких попадають в інтервал [2,10], а також відношення цих двох величин.

var a:array [1..4,1..4] of real;

i,j:integer;

s,p,z:real;

begin

{Цикл уведення}

writeln ('Уведення матриці розмірністю 4*4');

for i:=1 to 4 do

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

{Початкові значення й цикл обробки}

s:=0;

p:=1;

for i:=1 to 4 do

for j:=1 to 4 do begin

if a[i,j]>0 then s:=s+a[i,j];

if (a[i,j]>=2) and (a[i,j]<=10) then p:=p*a[i,j];

end;

{Вивід результатів}

writeln ('Сума=',s:6:2);

writeln ('Добуток=',p:6:2);

if p<>0 then begin

z:=s/p;

writeln ('Відношення=',z:6:2);

end

else writeln ('p=0, відношення обчислити не можна');

reset (input); readln;

end.

Оскільки про тип матриці в умові нічого не сказане, обраний "більш загальний" тип real. Спосіб уведення також обраний " за замовчуванням" – користувач уводить значення елементів матриці із клавіатури. Шукані сума, добуток і відношення позначені, відповідно, s, p і z.

 

Розглянемо приклад реалізації алгоритму другого типу. Відмінність його в тому, що алгоритм підрахунку або пошуку щораз заново реалізується у внутрішньому циклі, таким чином, шукана в подвійному циклі величина заново одержує значення на кожному кроці зовнішнього циклу.

Пр. Задана матриця C розмірністю 3*4. Знайти номер рядка, сума елементів якої максимальна.

Цього разу заповнимо матрицю випадковими числами. Позначимо s суму елементів чергового рядка, max – максимальну із цих сум. У якості шуканого номера рядка введемо целочисленную змінну imax, у яку будемо затягати номер поточного рядка i, якщо для цього рядка виконується співвідношення s>max. Змінну s випливає инициализировать на кожному кроці зовнішнього циклу по i (адже сума елементів шукається заново в кожному рядку), тоді як змінна max шукається для всієї матриці й инициализируется один раз до подвійного циклу:

var c:array [1..3,1..4] of real;

i,j,imax:integer;

s,max:real;

begin

{Формування матриці з одночасним виводом}

writeln;

write ('Матриця A');

for i:=1 to 3 do begin

writeln;

for j:=1 to 4 do begin

c[i,j]:=random*10 {речовинні числа від 0 до 10};

write (c[i,j]:6:2);

end;

end;

{Пошук номера рядка й вивід результату}

max:=-1e30;

for i:=1 to 3 do begin

s:=0;

for j:=1 to 4 do s:=s+c[i,j];

if s>max then begin

max:=s;

imax:=i;

end;

end;

writeln;

writeln ('Номер рядка=',imax);

end.

Аналогічно можуть бути реалізовані типові алгоритми в стовпці матриці – як описано вище, для цього досить поміняти місцями цикли по i і j.

 

Зустрічаються також завдання, де результати обробки рядків або стовпців матриці повинні бути занесені у вектор. Розв'яжемо завдання подібного типу.

Пр. У матриці S розмірністю 20*4 наведені оцінки 20 студентів за 4 іспиту останньої сесії. Підчитати й вивести на екран елементи вектора, що містить середні бали кожного студента.

Позначимо шуканий вектор як B. Його розмірність очевидна, оскільки кожний елемент вектора залежить від одного рядка матриці. Застосувавши алгоритм обробки рядків матриці, одержуємо наступну програму:

var s:array [1..20,1..4] of integer;

b:array [1..20] of real;

i,j:integer;

begin

{Формуємо матрицю з випадкових оцінок від 3 до 5}

for i:=1 to 20 do

for j:=1 to 4 do s[i,j]:=3+random(3);

{Обробляємо матрицю построчно, формуючи вектор B}

for i:=1 to 20 do begin

b[i]:=0;

for j:=1 to 4 do b[i]:=b[i]+s[i,j];

b[i]:=b[i]/4;

end;

{Виводимо оцінки разом із середніми балами}

writeln;

write ('Оцінки':8,' Бали');

for i:=1 to 20 do begin

writeln;

for j:=1 to 4 do write (s[i,j]:2);

write (b[i]:5:2);

end;

end.

 

Нерідко зустрічаються завдання, що вимагають обробки елементів, розташованих під або над головною діагоналлю матриці. Головна діагональ характеризується тим, що номера рядків і стовпців розташованих на ній елементів збігаються. Коректно це поняття визначене тільки для квадратних матриць із однаковим числом рядків і стовпців. Відповідно, для обробки елементів, що лежать на головній діагоналі матриці A розмірністю n*n було б досить циклу

for i:=1 to n do begin

{Обробка елемента A[i,i]}

end;

Коли потрібно обробити елементи, що лежать під головною діагоналлю ( для них номер рядка більше номера стовпця), подвійний цикл обробки міг би виглядати так:

for i:=1 to n do

for j:=1 to n do begin

if i>j then begin

{Обробка елемента A[i,j]}

end;

end;

Наведений код має той недолік, що в ньому виконуються свідомо зайві кроки циклу. Дійсно, для квадратної матриці розмірністю n*n ми виконуємо n2 кроків подвійного циклу, тоді як є всього елементів під головною діагоналлю. Виправити ситуацію може подвійний цикл зі змінною границею, подібний вивченому вище:

for i:=1 to n do

for j:=1 to i-1 do begin

{Обробка елемента A[i,j]}

end;

Цей цикл виконується рівно раз. Для обробки тільки елементів, що лежать над головною діагоналлю, цей цикл буде виглядати в такий спосіб:

for i:=1 to n do

for j:=i+1 to n do begin

if i>j then begin

{Обробка елемента A[i,j]}

end;

end;

 

Розповсюджений клас завдань, пов'язаних із множенням і обігом матриць, ми розглянемо в темі "Підпрограми". Зараз же обмежимося реалізацією добре відомого алгоритму множення матриці A розмірністю n*m на вектор b розмірності m. Нагадаємо, що в результаті множення виходить вектор розмірності n (позначимо його c), а саме множення полягає в тому, що кожний рядок матриці скалярно множиться на вектор по формулі й результат заноситься в компонент ci. Застосувавши відомі нам типові алгоритми, напишемо наступну програму:

const n=3;

m=4; {Прийняли конкретні розмірності матриці й векторів}

var a:array [1..n,1..m] of real;

b:array [1..m] of real;

c:array [1..n] of real;

i,j:integer;

begin

{Формуємо матрицю A з випадкових чисел і відразу виводимо}

writeln ('Матриця A');

for i:=1 to n do begin

for j:=1 to m do begin

a[i,j]:=random*10;

write (a[i,j]:6:2);

end;

writeln;

end;

{Аналогічно для вектора b}

writeln ('Вектор b');

for j:=1 to m do begin

b[j]:=random*10;

writeln (b[j]:6:2);

end;

{Множення сполучаємо з виводом вектора з - }

{це можливо, тому що компоненти вектора шукаються послідовно}

writeln ('Вектор c=A*b');

for i:=1 to n do begin

c[i]:=0; {c[i] - це СУМА, звідси початкове значення}

for j:=1 to m do

c[i]:=c[i]+A[i,j]*b[j];

writeln (c[i]:6:2);

end;

end.

 


<== попередня лекція | наступна лекція ==>
Оператор безумовного переходу | Підпрограми


Онлайн система числення Калькулятор онлайн звичайний Науковий калькулятор онлайн