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

Зубенко, Омельчук - Програмування. Поглиблений курс

.pdf
Скачиваний:
50
Добавлен:
07.03.2016
Размер:
4.72 Mб
Скачать

Розділ IV. АЛГОРИТМИ

5. Викреслюємо перший рядок і перший стовпчик. Повторюємо процедуру (кроки 1-5) n 1 разів, поки не отримаємо верхню трикут- ну матрицю.

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

7.Повторюємо крок 6 для всіх наступних рядків. У результаті отримуємо одиничну матрицю й розв'язок на місці вільного вектора.

Систему лінійних рівнянь будемо подавати в текстовому файлі (ко- ефіцієнти через пробіл, рядки з нового рядка) у вигляді

a0,0a0,1 a0,n 1 b0

K

an -1,0 an -1,1 an -1,n -1 bn -1 .

Приклад 4.10. Метод Гаусса Жордана.

Лістинг.

/*метод Гаусса – Жордана*/ #include <stdio.h> #include <process.h>

float **a, *b, *x;

int n; /*розмір матриці*/

char filename[256]; /*ім'я файла з матрицею; файл зберігається

водній директорії з програмою*/ FILE* InFile=NULL;

/*вивільняє пам'ять*/ void freeMatrix(){ int i;

for(i=0; i<n; i++){ delete [] a[i];

}

delete [] a; delete [] b; delete [] x;

}

void countNumLines(){

/*підраховує кількість рівнянь у системі*/ int nelf=0;

do{

nelf=0;

while(fgetc(InFile)!='\n' && !feof(InFile)) nelf=1;

491

ПРОГРАМУВАННЯ

if(nelf) n++;

} while(!feof(InFile));

}

void allocMatrix(){

/*виділяє пам'ять для масивів, що зберігають коефіцієнти рів-

нянь, вільні члени й розв'язки*/

 

 

 

 

int i,j;

 

 

 

 

 

 

x=new float[n];

 

 

 

 

 

 

b=new float[n];

 

 

 

 

 

 

a=new float*[n];

 

 

 

 

 

 

if(x==NULL || b==NULL || a==NULL){

to

allocate

for

%d

printf("\nNot

enough

memory

equations.\n", n);

 

 

 

 

 

 

exit(-1);

 

 

 

 

 

 

}

for(i=0; i<n; i++){ a[i]=new float[n]; if(a[i]==NULL){

printf("\nNot enough memory to allocate for %d equations.\n", n);

}

}

for(i=0; i<n; i++){ for(j=0; j<n; j++){

a[i][j]=0;

}

b[i]=0;

x[i]=0;

}

}

/*підставляє розв'язки в систему та друкує поруч те, що отримуємо в лівій частині рівняння. Для порівняння друкує поруч вільні члени. Два стовпчики мають збігатися*/

void testSolve(){ //test that ax=b int i=0,j=0; printf("\n"); for(i=0; i<n; i++){

float s=0; for(j=0; j<n; j++){

s+=a[i][j]*x[j];

}

printf("%f\t%f\n", s, b[i]);

}

}

492

Розділ IV. АЛГОРИТМИ

/*зчитує з файла коефіцієнти й вільні члени в масиви*/ void readMatrix(){

int i=0,j=0; for(i=0; i<n; i++){

for(j=0; j<n; j++){

fscanf(InFile, "%f", &a[i][j]);

}

fscanf(InFile, "%f", &b[i]);

}

}

/*виводить систему лінійних рівнянь на екран*/ void printMatrix(){

int i=0,j=0; printf("\n"); for(i=0; i<n; i++){

for(j=0; j<n; j++){ printf("%+f*X%d", a[i][j], j);

}

printf("=%f\n", b[i]);

}

}

/*друкує результат*/ void printResult(){

int i=0; printf("\n"); printf("Result\n"); for(i=0; i<n; i++){

printf("X%d=%f\n", i, x[i]);

}

}

/*робить так, щоб на головній діагоналі не було нулів*/ void diagonal(){

int i, j, k; float temp=0;

for(i=0; i<n; i++){ if(a[i][i]==0){

for(j=0; j<n; j++){ if(j==i) continue;

if(a[j][i] !=0 && a[i][j]!=0){ for(k=0; k<n; k++){

temp=a[j][k];

a[j][k]=a[i][k];

a[i][k]=temp;

}

493

ПРОГРАМУВАННЯ

temp=b[j];

b[j]=b[i];

b[i]=temp;

break;

}

}

}

}

}

int main(){

int i=0,j=0, k=0; do{

printf("\nInput filename:"); scanf("%s", filename); InFile=fopen(filename, "rt");

}while(InFile==NULL);

countNumLines();

allocMatrix();

rewind(InFile);

readMatrix();

diagonal();

fclose(InFile);

printMatrix(); for(k=0; k<n; k++){

for(i=k+1; i<n; i++){ if(a[k][k]==0){

printf("\nSolution is not exist.\n"); return 0;

}

float M=a[i][k]/a[k][k]; for(j=k; j<n; j++){

a[i][j]-=M * a[k][j];

}

b[i]-=M*b[k];

}

}

printMatrix(); for(i=n-1; i>=0; i--){

float s=0; for(j=i; j<n; j++){

s+=a[i][j]*x[j];

}

x[i]=(b[i] – s)/a[i][i];

}

494

Розділ IV. АЛГОРИТМИ

InFile=fopen(filename, "rt"); readMatrix(); fclose(InFile); printMatrix();

testSolve();

printResult();

freeMatrix(); scanf("%d", &i); return 0;

}

4.2.5. РОЗРІДЖЕНІ МАТРИЦІ

Часто доводиться виконувати операції з матрицями, більшість еле- ментів яких дорівнюють нулю. Такі матриці називаються розрідже- ними. Операції над ними та їх збереження можна реалізувати ефек- тивніше ніж у загальному випадку. Складність алгоритмів роботи з розрідженими матрицями зазвичай визначається кількістю ненульо- вих елементів матриці.

При збереженні розріджених матриць дотримуються двох основ- них вимог: збереження переважно ненульових елементів (інколи з не- великою кількістю нульових) і зручність оперування матрицями. Най- більшого поширення набули два способи зображення розрідженої m ×n матриці A . Перший повне рядкове зображення RR(C) (від англ. Row-wise Representation Complete). У ньому замість одного двовимір-

ного масиву використовуються три одновимірні:

1)AN масив ненульових елементів матриці A ;

2)JA масив індексів стовпчиків ненульових елементів матриці A ;

3)IA масив покажчиків; його i -та компонента вказує, з якої по- зиції масивів AN та JA починається опис i -го рядка матриці A . Остання компонента додаткова й указує на номер першої вільної по- зиції в масивах AN та JA.

У загальному випадку опис r-го рядка матриці A зберігається в компонентах від IA[r] до IA[r + 1]-1 масивів AN та JA. Якщо IA[r + 1]=IA[r], то це означає, що r-й рядок нульовий. Кількість елементів у масиві IA на одиницю більше кількості рядків початкової матриці, а в масивах JA та AN дорівнює кількості ненульових елеме- нтів початкової матриці. Цей формат називають повним, оскільки зображена вся матриця A .

495

ПРОГРАМУВАННЯ

Залежно від того, як записуються в кожному рядку індекси стовп- чиків у масиві JA (за порядком зростання чи ні), розрізняють упоряд- ковані й невпорядковані зображення.

Приклад 4.11. Зображення розрідженої матриці:

1

0

0

0

3

0

0

2

 

 

0 0 0 2 0 1 0 0

 

0

4

0

0

0

4

0

0

 

A =

7

6

0

0

8

0

0

1

.

 

 

0 0 0 0 0 0 0 9

 

0 0 5 0 0 0 0 0

 

Тут

AN=(1, 3, 2, 2, 1, 4, 4, 7, 6, 8, 1, 9, 5),

JA=(1, 5, 8, 4, 6, 2, 6, 1, 2, 5, 8, 8, 3),

IA=(1, 4, 6, 8, 12, 13, 14)

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

Другий спосіб подання розрідженої числової матриці n m спис- ковий. Він полягає в побудові n +m списків, вузли яких подають не- нульові елементи рядків і стовпчиків матриці, і двох масивів довжи- ною n та m , що задають голови цих списків. Кожен вузол це п'ятір- ка (i, j, a[i, j], ptr1, ptr 2), де i відповідно номер рядка, j номер

стовпчика, a[i, j] – сам ненульовий елемент матриці, ptr1 – покажчик на наступний ненульовий елемент у i -му рядку, ptr 2 – покажчик на наступний ненульовий елемент у j -му стовпчику.

Вибір структури даних для подання розрідженої матриці визнача- ється алгоритмом її обробки. Наприклад, для додавання матриць зру- чно задати їх у RR(C)-форматі. Тоді виконання операції зведеться до операції злиття ненульових елементів однакових за номером рядків обох матриць, якщо елементи в них мають різні позиції, і додавання елементів, розташованих на спільних позиціях. Обчислення добутку y = Ax полягатиме в підсумовуванні елементів масиву x , що зада-

ються елементами масиву JA, з ваговими коефіцієнтами, заданими елементами масиву AN, тобто yi = aij x j .

j: aij 0

*Література для CР: алгоритми лінійної алгебри – [65, 77, 151].

496

Розділ IV. АЛГОРИТМИ

Контрольні запитання та вправи

1.Описати алгоритми додавання, віднімання й прямого мно- ження матриць. Яка їхня часова складність?

2.Продемонструвати роботу алгоритму Штрассена при мно-

1

5

 

0

2

 

женні матриць

3

2

 

та

 

 

.

 

 

10

4

 

3.Написати програму, що обчислює добуток матриць методом Винограда. Вхідними параметрами є розмірність матриці й сама матриця.

4.Протестувати програму з прикл. 4.10. Скористатися систе- мами рівнянь із вправи 42 підрозд. 3.4.

5.Що таке RR(C)-зображення розрідженої матриці?

6.Що таке спискове зображення розрідженої матриці?

7.Для розрідженої матриці A побудувати RR(C)-зображення:

 

0 0 5 0 4 0 0

 

1 0 5 0 0 0

 

 

 

 

 

0 0 6 0 0 1 0

 

 

0 0 0 0 0 0

 

 

 

 

 

 

 

 

 

 

a)

A =

0 0 0 0 0 0 0

; б) A =

0 0 0 0 4 0

;

 

 

 

 

 

 

 

 

 

 

 

 

 

1 9 7 0 0 0 0

 

1 5 2 0 0 0

 

 

 

 

 

0 0 0 0 0 0 0

 

 

0 0 1 0 0 0

 

 

 

 

 

 

 

 

 

 

 

0 0 1 2 0 0 0

 

0 0 0 9 8 0 0 7

 

 

 

0 0 0 3 0 4 0

 

 

0 0 6 0 0 5 0 0

 

 

 

 

 

 

 

 

в) A =

0 0 0 0 5 0 0

 

; г) A =

0 0 0 0 0 0 0 0

 

;

 

 

6 0 7 0 0 0 0

 

 

4 0 3 0 0 0 0 2

 

 

 

 

 

 

 

 

 

 

0 0 0 0 0 0 0

 

 

0 0 0 0 0 0 0 1

 

 

 

 

 

 

 

 

0 0 1 2 1 0

 

0 1 0 0 0 0 0

 

 

0

0

9

0

0

0

 

 

0 0 3 0 5 0 0

 

 

 

 

 

 

0

0

2

0

3

0

 

; е) A =

0 0 0 7 0 0 0

.

д) A =

 

 

 

 

 

 

 

 

0

0

0

0

0

0

 

 

0

0

9

0

0

0

0

 

 

0

0

0

0

0

0

 

 

 

 

0 0 0 0 0 0 0

 

 

 

 

 

0

0

4

5

0

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

8.Для RR(C)-зображення відновити матрицю A (з точністю до нульових стовпчиків праворуч):

а) AN=(1, 13, 5, 7, 9, 1, 2), JA=(1, 2, 4, 1, 3, 5, 6),

IA=(1, 4, 6, 7, 8);

б) AN=(1, 2, 3, 4, 5, 6), JA=(1, 2, 3, 4, 5, 6),

IA=(1, 4, 5, 6, 6);

в) AN=(9, 10, 5, 3, 2, 1, 6), JA=(1, 5, 7, 3, 2, 1, 7),

IA=(1, 3, 3, 5, 6, 7);

497

ПРОГРАМУВАННЯ

г) AN=(1, 2, 5, 4, 3, 2, 9), JA=(1, 5, 4, 1, 3, 5, 4),

IA=(1, 3, 5, 7, 8);

д) AN=(1, 1, 2, 4, 6, 2, 5), JA=(1, 2, 4, 1, 3, 5, 6),

IA=(1, 4, 6, 8, 8);

е) AN=(5, 3, 5, 3, 5, 3), JA=(1, 2, 4, 1, 3, 5),

IA=(1, 3, 5, 6, 7).

9.Написати програму, яка на вході отримує зображення мат- риці у вигляді двовимірного масиву, а на виході видає її RR(C)-зображення.

10.Написати програму, яка на вході отримує RR(C)-зображення матриці, а на виході видає цю матрицю у вигляді двовимір- ного масиву.

11.Написати програму, що на вході отримує два RR(C)- зображення розріджених матриць однакової розмірності й підраховує їхні:

а) суму; б) різницю; в) добуток.

Результат подати у вигляді RR(C)-зображення. Визначити кіль- кість використаних дій додавання, віднімання, множення.

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

4.3.Списки, стеки, черги й бінарні дерева

¾Списки

¾Стеки

¾Черги

¾Бінарні дерева

¾Контейнерні типи

Ключові слова: динамічні структури даних, зв'язаний список, голова списку, однозв'язний лінійний список, двозв'язний список, загальний список, стек і черга як масив і список, операції empty, pop та push для стеків і черг, бінарне дерево, обхід бінарного дерева, послідовні контейнерні типи векторів, списків і двосторонніх черг, асоційовані контейнерні типи відношень і множин, ітератор, стандартні узагаль- нені алгоритми

498

Розділ IV. АЛГОРИТМИ

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

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

4.3.1. СПИСКИ

Списки означені в підрозд. 1.4.4. Вони реалізуються за допомогою зв'язаних списків, які можуть бути однозв'язними та двозв'язними. Елементом однозв'язного списку є структура, що окрім ключа та ін- шої інформації містить покажчик на наступний елемент списку. По- кажчиком в останньому елементі списку є NULL. Двозв'язний список містить покажчик як на наступний, так і на попередній елементи списку. Вибір типу списку залежить від конкретної задачі.

Однозв'язний лінійний список. У такому списку інформаційне поле кожного з елементів містить тільки ключ певний атом. Сам атом має фіксовану (нединамічну) структуру. Це може бути число, слово, таблиця тощо. У такому списку можна пересуватися лише в одному напрямку від голови (першого елемента) у його кінець. Щоб задати лінійний список, достатньо вказати адресу його голови:

 

next

 

next

next

head

дані

 

 

дані

 

дані

 

дані

 

 

 

 

 

 

 

 

 

Покажемо, як реалізуються основні операції для лінійних списків. Приклад 4.12. Опис лінійного списку:

Лістинг.

struct Item {

int data; /*інформаційне поле*/

struct Item *next; /*покажчик на наступний елемент*/ };

typedef struct Item *Itemptr; /*введення імені Itemptr для типу покажчиків на структуру Item*/

499

ПРОГРАМУВАННЯ

Itemptr HeadList; /*покажчик на голову списку*/

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

Приклад 4.13. Друкування елементів лінійного списку:

Лістинг.

/*друкування значень елементів списку*/ void display(Itemptr start)

{

while(start) { printf("%d\t", start->data); start=start->next;

}

printf("\n");

}

При виклику функції display параметр start задає голову списку, потім функція переходить до наступного елемента, на який указує поле next. Процес припиняється, коли next дорівнює нулю.

Для отримання елемента зі списку потрібно просто пройти низкою посилань. Розглянемо приклад функції пошуку за ключовим полем data.

Приклад 4.14. Пошук за ключем:

Лістинг.

/*пошук попередника елемента*/

Itemptr getPrev(Itemptr start,Itemptr elem)

{

if(elem){

Itemptr prev=NULL; Itemptr p=start; while((p)&&(p!=elem)) {

prev=p; p=p->next;

}

return prev;

} else return NULL; /*елемент не знайдено*/

}

/*пошук попередника елемента за значенням; результат – NULL, якщо елемент не знайдено або він у голові списку*/

500