13.7. Операції з матрицями
Матричні обчислення відіграють значну роль при розв’язанні прикладних задач. Вони покладені в основу математичного апарата квантової й статистичної механіки й фізики, радіоелектроніки, теорії коливань, де фундаментальне значення мають характеристичні рівняння, власні числа й вектори. Алгоритми лінійної алгебри є складовою частиною алгоритмів розв’язування дуже великої кількості лінійних і нелінійних задач моделювання і обробки даних, задач оптимізації, багатьох інших задач у наукових дослідженнях, технічних розробках, розв’язуванні економічних проблем. У даному підрозділі розглядаються декілька алгоритмів лінійної алгебри, які використовуються найбільш часто.
Звертаємо увагу на те, що у загально-математичних формулюваннях нумерація елементів масивів починається з 1, в той час як у програмних реалізаціях, які наводяться, нумерація, згідно з традиціями мови С, починається з 0.
Задача обчислення сідлової точки матриці
Задана прямокутна матриця А розміром NM. Потрібно обчислити її мінімаксну сідлову точку за формулою :
, (1)
або максимінну сідлову точку:
. (2)
Алгоритм
Алгоритм випливає прямо з формул (1) і (2).
Нижче наведені дві функції, які повертають значення сідлових точок матриці з елементами типу double.
// Приклад 1
double MinMax (double** A, int N, int M )
{ int i,j; double minmax;
double* D = new double[N]; // створення допоміжного масиву
for (i=0; i<N; i++)
{ D[i] = A[i][0];
for (j=1; j<M; j++) if (A[i][j]>D[i]) D[i] = A[i][j];
}
// знайти найменший серед найбільших
minmax = D[0];
for (i=1; i<N; i++) if (D[i] < minmax) minmax = D[i];
delete[] D;
return minmax;
}
// Приклад 2
double MaxMin (double** A, int N, int M )
{ int i,j; double maxmin;
double* D = new double[N];
for (i=0; i<N; i++)
{ D[i] = A[i][0];
for (j=1; j<M; j++) if (A[i][j]<D[i]) D[i] = A[i][j];
}
maxmin = D[0];
for (i=1; i<N; i++) if (D[i] > maxmin) maxmin = D[i];
delete [] D;
return maxmin;
}
Задача обчислення визначника матриці
Задана квадратна числова матриця:
. (3)
Обчислити її визначник det(A).
Алгоритм
До еквівалентних перетворень відносять такі перетворення, які не змінюють визначник матриці. Використовуючи певну послідовність еквівалентних перетворень, вихідну матрицю можна звести до трикутного вигляду:
. (4)
Визначник такої матриці легко обчислюється шляхом отримання добутку діагональних елементів:
det (A) = det (A') = . (5)
Якщо при виконанні перетворень матриці виконувались операції перестановки рядків, то формула для обчислення визначника дещо змінюється:
, (6)
де p - кількість операцій парної перестановки рядків.
Для того, щоб перетворити елемент матриці akj, що лежить нижче головної діагоналі, у нуль, треба отримати нові елементи k-того рядка за такою формулою:
,. (7)
З останньої формули видно, що діагональний елемент aii повинен бути відмінним від нуля. Щоб цього уникнути, і, одночасно, збільшити точність і надійність обчислення визначника, застосовується прийом, названий «вибором головного елемента». Цей прийом полягає в наступному. Припустимо в i-м стовпці необхідно перетворити в нуль елементи, що лежать нижче головної діагоналі. Серед елементів цього стовпця, починаючи з aii і до останнього елемента ani, знаходимо максимальний по модулю елемент. Потім міняємо місцями i-тий рядок і рядок, що містить максимальний по модулю елемент, місцями. Таким чином, на місці діагонального елемента завжди буде максимальний (по абсолютній величині) з можливих елементів. Якщо знайдений максимальний елемент дорівнює нулю, то обчислення можна завершити: визначник у цьому випадку буде дорівнювати нулю.
Нижче наводиться приклад функції DetMatr(A,N), яка обчислює визначник матриці A розміром NN. Обчислення виконуються відповідно до дійсного типу double.
// Приклад 3
double DetMatr(double** A, unsigned N )
{ int i, j, k=0;
double D, R;
double** C= new double*[N]; // створення робочої
for (i=0; i<N; i++) C[i]=new double[N]; // матриці С
for (i=0; i<N; i++)
for (j=0; j<N; j++) C[i][j] = A[i][j];
// копіювання матриці
for (i=0; i<N-1; i++) // основний цикл
{ // вибираємо головний елемент
D=fabs(C[i][i]);
k=i;
for (j=i+1; j<N; j++)
if (fabs(C[j][i])>D) { k = j; D = fabs(C[j][i]); }
// міняємо, якщо треба, рядки місцями
if (k!=i) swp(C[k],C[i]);
// перетворення в нуль елементів стовпця R = 1./C[i][i];
for (k=i+1; k<N; k++)
{ D = C[k][i]*R;
for (j=i; j<N; j++) C[k][j] = C[k][j]- D*C[i][j];
}
}
// отримаємо визначник як добуток елементів // головної діагоналі
D= 1.0;
for (i=0; i<N; i++) D *= C[i][i];
for (i=0; i<N; i++) delete[] C[i]; // звільнення пам’яті
delete[] C;
return D;
}
Задача обчислення оберненої матриці
Надана квадратна числова матриця А з розмірами nn. Отримати матрицю A-1, яка відповідає такій умові:
A A-1 = A-1 A = I , (8)
де I - одинична діагональна матриця.
Алгоритм
Створимо робочу прямокутну матрицю С розміром n2n, приписавши праворуч до вихідної матриці A одиничну діагональну матрицю E такого ж розміру:
. (9)
Виконаємо над цією розширеною матрицею перетворення, мета яких - перетворити у нулі всі елементи, що лежать нижче головної діагоналі лівої частини матриці. Операції перетворення рядків виконуємо над рядками розширеної матриці в цілому, включаючи одиничну підматрицю. Перетворення проводяться в три етапи:
1. Перетворюємо в нулі всі елементи підматриці А, які лежать нижче головної діагоналі.
2. Перетворюємо в нулі всі елементи підматриці А, що лежать вище головної діагоналі.
3. Перетворюємо всі діагональні елементи підматриці А в одиниці, шляхом ділення кожного рядка розширеної матриці на відповідний діагональний елемент.
Після виконання цих кроків, підматриця А стане одиничною, а підматриця Е перетвориться в шукану обернену матрицю А-1. У наведеному алгоритмі можна також застосувати вибір головного елемента, відповідна операція виконується на етапі 1.
Нижче наведено функцію ObrMatr, яка реалізує поданий вище алгоритм. Вхідна матриця зберігається в масиві А, зворотна записується в масив В, N - розмір матриці. Двовимірний масив B відповідних розмірів повинен бути створений заздалегідь.
// Приклад 4
Yandex.RTB R-A-252273-3- Запоріжжя знту 2008
- Глава 1
- 1.1. Історія та сучасність
- 1.2. Загальна структура програми. Два простих приклади
- Void main()
- Void main()
- Глава 2 Об’єкти та ідентифікатори
- 2.1. Об'єкти та їхні атрибути
- 2.2. Алфавіт мови та лексеми
- 2.3. Ідентифікатори
- Void main() // (рівень 0)
- 2.4. Вправи
- Глава 3
- 3.1. Поняття виразу. Вирази Lvalue та Rvalue
- 3.2. Операції. Пріоритети та асоціативність
- Void main()
- Void main()
- Void main()
- 3.3. Вправи
- Глава 4
- 4.1. Види операторів
- 4.2. Стандартні оператори
- If (лв) опер_1; [ else опер_2; ]
- Void main()
- If (лв1) опер_1;
- Void main()
- Void main()
- Void main()
- Void main()
- Void main()
- 4.3. Оголошення змінних та ініціалізація
- Int number(125);
- Int number(125);
- 4.4. Константи і константні об'єкти
- Void main()
- 4.5. Вправи
- Глава 5
- 5.1. Типи та їхні різновиди
- 5.2. Службове слово void
- Int a[small], a[large];
- 5.4. Перетворення типів
- 5.5. Вправи
- Void main()
- Глава 6 покажчики і посилання
- 6.1. Покажчики
- Void main()
- Void strcpy(char* s1, char* s2)
- Void* pv;
- 6.2. Посилання
- 6.3 Вправи
- Void main()
- Глава 7 масиви і динамічні об'єкти
- 7.1. Масиви
- Void main()
- 7.2. Рядки символів
- Int strlen(char* s);
- Int strcmp(char* s1, char* s2);
- Int len(char *s)
- 7.3. Динамічні об'єкти й масиви
- Void main()
- Void main()
- Void main()
- Void main()
- 7.4. Вправи
- Глава 8 функції та процедури
- 8.1. Загальні відомості
- Void main()
- Void c_mul(double a_re, double a_im,
- Void swap1(long *px, long *py)
- Void swap2(long &X, long &y)
- Void main()
- Int fun(int, float*, double&);
- Void fun(int n)
- Void main()
- Void swap(int& a, int& b)
- 8.2. Функція main
- Void або int main(int n, char** p, char** q);
- Void main(int n, char** p, char** q)
- 8.3. Функції зі змінною кількістю параметрів
- Void main()
- 8.4. Покажчики на функції
- Int (*pf[3])(float X, float y);
- Void main()
- 8.5. Функції з шаблонами
- Void swap(string& s1, string& s2)
- Void main()
- Inline t abs(t X)
- Void create(type* &a, int n)
- Void del(type* &a)
- 8.6. Вправи
- Глава 9 консольне вВедення / вИведення
- 9.1. Засоби бібліотеки с
- Void main()
- Void main()
- Void main()
- 9.2. Використання потоків
- Void main()
- Void main()
- 9.3. Вправи
- Глава 10 операції з файлами
- 10.1. Види файлів і режими роботи з ними
- 10.2. Бібліотека с
- Void fprint(file* f, &X)
- Void rewind(file* f);
- Void main()
- Void main(int n, char** f)
- Void main(int n, char** fnam)
- 10.3. Застосування потоків
- Ifstream fin;
- Void open(char* filename, int mode, int access);
- Ifstream fin("a.Dat");
- Void main()
- Ifstream fa("a.Dat");
- If (fa.Eof()) break;
- Void main(int n, char** fnam)
- Void main(int n, char** f)
- Ifstream fa(f[1]);
- Void main(int n, char** fnam)
- Void main()
- Ifstream in("example.Cpp");
- Void main()
- Ifstream in("name.Dat",ios::binary);
- Void main()
- Ifstream fa("a.Dat");
- Int descr(fstr& f)
- Void main()
- Ifstream ina(infa);
- Ifstream inb(infb);
- 10.4. Вправи
- Глава 11 компіляція програми. Директиви і макроси
- 11.1. Фази компіляції
- 11.2. Директиви режиму компіляції
- 11.3. Директиви препроцесора
- 11.4. Вправи
- Глава 12 змішане програмування. Використання ассемблерного коду
- 12.1. Засоби використання асемблера
- Void main()
- 12.2. Вправи
- Глава 13 програмна реалізація алгоритмів
- 13.1. Алгоритм Евкліда пошуку найбільшого загального дільника двох цілих чисел
- 13.2. Обчислення факторіала
- 13.3. Пошук простих чисел. Решето Ератосфена
- Void main()
- 13.4. Генерація підмножин
- Void main()
- 13.5. Сортування масивів
- Void main()
- Void main()
- Int flag;
- 13.6. Пошук у масиві
- Void main()
- Void main()
- 13.7. Операції з матрицями
- Void ObrMatr (double** a, double** b, unsigned n )
- Void Minv(double** a, double** b, unsigned n )
- Void Gauss(double** a, double* b, double* X, unsigned n)
- 13.8. Лінійна інтерполяція даних
- 13.9. Лінійна апроксимація
- Void linappr(int n, double* X, double* y, double& a0, double& a1)
- 13.10. Розв’язування нелінійних рівнянь
- Void Bisec(funx f, double a, double b, double eps, double& X)
- Void Bisec(funx f, double a, double b,
- Void main()
- 13.11. Пошук заданої послідовності символів у файлі
- Void push(char* s, int n, char X)
- Void main()
- 13.12. Вправи
- Глава 14 створення й використання бібліотечних модулів. Модуль syst.H
- 14.1. Створення бібліотечних модулів
- 14.2. Модуль syst.H
- 14.3. Системні функції та макрооперації
- Void runtimer();
- Void main()
- Void runstimer();
- Void main()
- Void swp(Type& a, Type& b);
- Void main()
- Int cmp(int nx, int ny, Type* X, Type* y);
- Void errhalt(bool ex, char* mes);
- Void errhalt(bool ex, char* mes, file* f);
- 14.4. Операції введення/виведення
- Void flushkey();
- Void main()
- Int getyes();
- Void main()
- Void main()
- Int weight(type X);
- Void main()
- Int hamdist(Type a, Type b)
- 14.6. Спеціальні класи. Клас Spline для інтерполяції даних сплайнами
- Void main()
- 14.7. Вправи
- Глава 15 графІчне виведення. Модуль rgraph.H
- 15.1. Модуль rgraph.H. Загальні відомості
- 15.2. Глобальні змінні й константи
- 15.3. Класи й покажчики на функції
- 15.4. Функції й процедури класів
- Void main()
- Void main()
- Void main()
- Void main()
- 15.7. Вправи
- Глава 16 Життєвий цикл програмного продукту. Питання стилю запису програм
- 16.1. Життєвий цикл програми
- Int n, alfa, col;
- Void draw();
- Void rotate(int delta);
- 16.3. Вправи
- Література