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

3.4. Ефективність масивів

Масиви ефективні при звертанні до довільного елемента, яке відбувається за постійний час ( (1)), однак такі операції як додавання та видалення елемента, потребують часу (n), де n – розмір масиву. Тому масиви переважно використовуються для зберігання даних, до елементів яких відбувається довільний доступ без додавання або видалення нових елементів, тоді як для алгоритмів з інтенсивними операціями додавання та видалення, ефективнішими є зв’язані списки.

Інша перевага масивів, яка є досить важливою – це можливість компактного збереження послідовності їх елементів в локальній області пам’яті (що не завжди вдається, наприклад, для зв’язаних списків), що дозволяє ефективно виконувати операції з послідовного обходу елементів таких масивів.

Масиви є дуже економною щодо пам’яті структурою даних. Для збереження 100 цілих чисел у масиві треба рівно у 100 разів більше пам’яті, ніж для збереження одного числа (плюс, можливо, ще декілька байтів). У той же час, усі структури даних, які базуються на вказівниках, потребують додаткової пам’яті для збереження самих вказівників разом із даними. Однак, операції з фіксованими масивами ускладнюються тоді, коли виникає необхідність додавання нових елементів у вже заповнений масив. Тоді його слід розширювати, що не завжди можливо і для таких задач слід використовувати зв’язані списки, або динамічні масиви.

У випадках, коли розмір масиву є досить великий і використання звичайного звертання за індексом стає проблематичним, або великий відсоток його комірок не використовується, треба звертатися до асоціативних масивів, де проблема індексування великих об’ємів інформації вирішується оптимальніше. В асоціативному масиві замість числових індексів використовуються ключі будь-яких типів. Дані в асоціативному масиві так само можуть бути різнотипними. Така структура також відома як «хеш» або як «словник». Це лише відображення «назва-значення», як показано нижче:

h = {1 => 2, «2» => «4»}

print hash,»\n»

print hash[1],»\n»

print hash[«2»],»\n»

print hash[5],»\n»

З тої причини, що масиви мають фіксовану довжину, треба дуже обережно ставитися до процедури звертання до елементів за їхнім індексом, тому що намагання звернутися до елемента, індекс якого перевищує розмір такого масиву (наприклад, до елемента з індексом 6 у масиві з 5 елементів), може призвести до непередбачуваних наслідків.

Треба також бути уважним щодо принципів нумерації елементів масиву, яка в одних мовах програмування може починатись з 0, а в інших – з 1.

3.5. Зберігання багатовимірних масивів

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

Найпоширеніші способи його організації в пам’яті наведео нижче.

Розташування «рядок за рядком». Це найбільш уживаний на сьогодні спосіб, який зустрічається у більшості мов програмування:

1 2 3 4 5 6 7 8 9

Розташування «стовпчик за стовпчиком». Такий метод розташування масивів використовується, зокрема, в мові програмування Fortran:

1 4 7 2 5 8 3 6 9

Масив із масивів. Багатовимірні масиви репрезентуються одновимірними масивами вказівників на одновимірні масиви. Розташування може бути як «рядок за рядком», так і «стовпчик за стовпчиком».

Перші два способи дозволяють розміщувати дані компактніше (мають більшу локальність), однак це одночасно і обмеження: такі масиви мають бути «прямокутними», тобто кожний рядок має містити однакову кількість елементів. Розташування «масив із масивів», з іншого боку, не дуже ефективне щодо використання пам’яті (необхідно зберігати додатково інформацію про вказівники), але знімає обмеження на «прямокутність» масиву.

Особливим видом масивів є розріджений масив (матриця), в якому дані подані не незперервно, а фраґментарно. Алгоритми опрацювання розріджених матриць передбачають дії тільки з ненульовими елементами і, отже, кількість операцій буде пропорційна кількості ненульових елементів.

Існують різні методи зберігання елементів матриці в пам’яті, наприклад, лінійний зв’язний список. Можна зберігати матрицю, використовуючи кільцевий зв’язний список, двонапрямлені стеки і черги (детальніше розглянуто далі). Існує діагональна схема зберігання симетричних матриць, а також зв’язні схеми розрідженого зберігання. Зв’язна схема зберігання матриць, запропонована Кнутом [7, 8], пропонує зберігати в масиві (наприклад, в AN) в довільному порядку самі елементи, індекси рядків і стовпців відповідних елементів (наприклад, в масивах I і J), номер (з масиву AN) наступного ненульового елемента, розташованого в матриці у рядку (NR) і у стовпці (NC), а також номера елементів, з яких починається рядок (вказівники для входу в терміні – JR) і номери елементів, з яких починається стовпець (вказівники для входу в стовпець – JC). Така схема зберігання надлишкова, але дозволяє легко здійснювати будь-які операції з елементами матриці.

Найширше вживана схема зберігання розріджених матриць – це схема, запропонована Чангом і Густавсоном: «розріджений рядковий формат» [22]. Ця схема висуває мінімальні вимоги до пам’яті і дуже зручна при виконанні операцій додавння, множення матриць, транспонування, розв’яузування систем лінійних рівнянь, при зберіганні коефіцієнтів у розріджених матрицях і т.ін. У цьому випадку значення ненульових елементів зберігаються в масиві AN, відповідні їм індекси стовпців – у масиві JA. Крім того, використовується масив вказівників, наприклад IA, що відзначають позиції AN і JA, з яких починається опис чергової стрічки. Додатковий компонент в IA містить вказівник першої вільної позиції в JA і AN.

На практиці для роботи з розрідженим масивом розробляються функції:

а) для перетворення індексів масиву в індекс вектора;

б) для визначення значення елементу масиву з його запакованого подання за двома індексами (рядок, стовпчик);

в) для запису значення елементу масиву в його запакованому поданні за двома індексами.

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

L=((y-1)*XM+x) /2),

де L – індекс у лінійному поданні; x, y – відповідно рядок та стовпчик у двовимірному поданні; XM – кількість елементів у рядку початкової матриці.

Програмна реалізація цього методу така (значення XM відоме):

Function PutTab(у,x,value: integer): boolean;

Function GetTab(x,y: integer): integer;

Var arrp: array[1..XM*XM div 2] of integer;

Function NewIndex(y, x : integer): integer;

var i: integer;

begin NewIndex:=((y-1)*XM+x) div 2); end;

Function PutTab(у,x,value : integer): boolean;

begin

if NOT ((x mod 2<>0) and (y mod 2<>0)) or

NOT ((x mod 2=0) and (y mod 2=0)) then begin

arrp[NewIndex(у,x)]:=value; PutTab:=true; end

else PutTab:=false;

end;

Function GetTab(x,y: integer): integer;

begin

if ((x mod 2<>0) and (y mod 2<>0)) or

((x mod 2=0) and (y mod 2=0)) then GetTab:=0

else GetTab:=arrp[NewIndex(у,x)];

end;

end.

Стисле подання матриці зберігається у векторі arrp.

Функція NewIndex виконує перерахунок індексів за наведеною вище формулою і повертає індекс елемента у векторі arrp.

Функція PutTab виконує зберігання у стислому поданні одного елемента з індексами x, у і значенням value. Зберігання виконується тільки у тому випадку, якщо індекси x, у адресують не нульовий елемент. Якщо зберігання виконане, функція повертає true, інакше – false.

Для доступу до елемента за індексами двовимірної матриці використовується функція GetTab, яка за індексах x, у повертає вибране значення. Якщо індекси адресують нульовий елемент матриці, функція повертає 0.

Розглянемо дещо інший спосіб ефективного зберігання розрідженої матриці: для матриці створюється дескриптор – масив desc, який заповнюється при ініціалізації матриці таким чином, що i-ий елемент масиву desc містить індекс першого елементу i-ого рядка матриці у її лінійному поданні. Такий метод спрощує доступ до кожного елемента матриці (функція NewIndex): за номером рядка з дескриптора відразу вибирається індекс початку рядка і до нього додається зсув елемента зі стовпчика x. Процедури PutTab і GetTab – такі ж, як і в попередній реалізації, тому тут не наводяться.

Function PutTab(у,x,value : integer): boolean;

Function GetTab(x,y: integer): integer;

Procedure InitTab;

Var arrp: array[1..XM*XM div 2] of integer;

desc: array[1..XM] of integer;

Procedure InitTab;

var i : integer;

begin

desc[1]:=0; for i:=1 to XM do desc[i]:=desc[i-1]+XM;

end;

Function NewIndex(у, x : integer): integer;

var i: integer;

begin NewIndex:=desc[y]+x div 2; end;

end.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]