Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Основная книга по С++й.doc
Скачиваний:
16
Добавлен:
28.10.2018
Размер:
2.07 Mб
Скачать

Int Cubic (double *X, double a, double b, double c)

{

double q, r, r2, q3;

q = (a * a - 3. * b) / 9.;

r = (a * (2. * a * a - 9. * b) + 27.*c) / 54.;

r2 = r * r;

q3 = q * q * q;

if (r2 < q3)

{

double t = acos(r / sqrt (q3));

a /= 3.;

q = -2. * sqrt (q);

x[0] = q * cos (t / 3.) - a;

x[1] = q * cos ((t + M_2PI) / 3.) - a;

x[2] = q * cos ((t - M_2PI) / 3.) - a;

return (3);

}

else

{

double aa, bb;

if (r <= 0.) r =- r;

aa = -pow (r + sqrt (r2 - q3), 1. / 3.);

if(aa != 0.) bb = q / aa;

else bb = 0.;

a /= 3.;

q = aa + bb;

r = aa - bb;

x[0] = q - a;

x[1] = (-0.5) * q - a;

x[2] = (sqrt (3.) * 0.5) * fabs (r);

if (x[2] == 0.) return (2);

return (1);

}

}

Пример 14. Алгоритм Краута (нижне-верхняя (LU) декомпозиция матрицы).

Алгоритм Краута  это фактически метод решения систем общего вида, конкурирующий по быстродействию с общеизвестным методом Гаусса-Жордана, но позволяющий более эффективно использовать решение.

Если мы можем разложить матрицу линейной системы A в произведение A=L*U, где L  нижняя, а U  верхняя треугольные матрицы, то решение системы уравнений с произвольной правой частью производится весьма просто, применением двух обратных подстановок. Более того, в отличие от известного метода Гаусса-Жордана, разложенная матрица позволяет быстро решать серии линейных уравнений с различными правыми частями при одной и той же матрице.

Метод Краута позволяет провести LU-декомпозицию матрицы примерно за то же число операций, что и "прямая" часть метода Гаусса-Жордана. Итоговые коэффициенты двух треугольных матриц упаковываются в матрицу того же размера, что и A, и на том же месте в памяти; при этом верхняя матрица U размещается в наддиагональной части и на диагонали; нижняя L в поддиагональной части, а диагональные элементы L считаются все равными 1 (без ограничения общности) и не выводятся.

Алгоритм Краута представляет из себя следующее:

    1. Положить Lii=1 i=0,...,N-1 (здесь ничего не производится, а только имеется в виду для дальнейших шагов;

    2. Для каждого j=0,1,...,N-1 проделать:

  1. Для всех i=0,...,j вычислить Uij:

Uij=Aij - SUM(0<=k<i)(Lik*Uik) (при i=0 сумму полагать нулем);

  1. Для всех i=j+1,...,N-1 вычислить:

Lij = Aij - SUM(0<=k<j)(Lik*Uik))/Uii

Обе процедуры выполняются до перехода к новому j.

Устойчивая работа алгоритма Краута возможно только при условии выбора ведущего элемента для каждого обращения к j из п.2 алгоритма. Выбор производится перед выполнением п.2 для очередного j перестановки необработанных строк матрицы так, чтобы строка, подставляемая на место j, содержала наибольший по модулю элемент в колонке j. Порядок перестановки необходимо запомнить, для чего достаточно использовать дополнительный вектор целых величин. Еще один вектор используется внутри алгоритма для масштабирования.

Алгоритм Краута имеет несколько приложений, одно из которых  решение системы линейных уравнений (обратная подстановка с учетом порядка перестановки строк), другие  вычисление обратной матрицы и вычисление детерминанта. Подробнее вызовы функций, связанных с алгоритмом Краута и реализация деталей алгоритма находятся в комментариях к программе и в ней самой.

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include <conio.h>

#include <string.h>

#define TINY 1.e-30

/*

LU-декомпозиция в соответствии с алгоритмом Краута.

Описание:

int LU_decompos(double **a,int n,int *indx,int *d,double *vv);

Параметры:

a - исходная матрица (n x n) на входе;

n - размер матрицы;

indx - массив целых чисел (размера n) для запоминания перемещений;

d - на выходе, содержит +1 или -1 для четного или нечетного числа перемещений.

vv - временный массив (size n).

Результат:

0 - некорректная входная матрица (непригодна для декомпозиции),

1 - положительный результат.

Обратная замена, использующая LU-декомпозицию матрицы.

Описание:

void LU_backsub(double **a,int n,int *indx,double *b);

Параметры:

a - матрица, разложенная по Крауту;

n - размер матрицы;

indx - порядок перемещения, полученный алгоритмом декомпозиции;

b - вектор (размера n).

Примечание: a и indx не изменяются в этой функции и могут быть использованы повторно.

Инвертирование матрицы с использованием LU-разложенной матрицы.

Описание:

void LU_invert(double **a,int n,int *indx,double **inv,double *col);

Параметры:

a - матрица, разложенная по Крауту;

n - размер матрицы;

indx - порядок перестановки, полученный алгоритмом декомпозиции;

inv - матрица назначения;

col - временный массив (размера n).

Получение матрицы, полученной с использованием LU-разложенной матрицы

Описание:

double LU_determ(double **a,int n,int *indx,int *d);

Параметры:

a - матрица, разложенная по Крауту;

n - размер матрицы;

indx - порядок перемещения, полученный алгоритмом декомпозиции;

d - знак равенства (+1 или -1) полученный при декомпозиции.

Результат: определяющее значение.

*/

/* декомпозиция */

int LU_decompos (double **a, int n, int *indx, int *d, double *vv)

{

register int i, imax, j, k;

double big, sum, temp;

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

{

big = 0.;

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

if ((temp = fabs (a[i][j])) > big) big = temp;

if (big == 0.) return (0);

vv[i] = big;

}

/* главный цикл алгоритма Краута */

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

{ /* это часть a) алгоритма, исключая i==j */

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

{

sum = a[i][j];

for (k = 0; k < i; k++) sum -= a[i][k] * a[k][j];

a[i][j] = sum;

}

/* инициализация для поиска наибольшего элемента */

big = 0.;

imax = j;

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

{

sum = a[i][j];

for (k = 0; k < j; k++) sum -= a[i][k] * a[k][j];

a[i][j] = sum;

if ((temp = vv[i] * fabs (sum)) >= big)

{

big = temp;

imax = i;

}

}

/* обмен строк, если нужен, смена равенства и размера множителя */

if (imax != j)

{

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

{

temp = a[imax][k];

a[imax][k] = a[j][k];

a[j][k] = temp;

}

*d =- (*d);

vv[imax] = vv[j];

}

/* сохраняем индекс */

indx[j] = imax;

if (a[j][j] == 0.) a[j][j] = TINY;

if (j < n - 1)

{

temp = 1. / a[j][j];

for (i = j+1; i < n; i++) a[i][j] *= temp;

}

}

return (1);

}

/* обратная замена */