Формирование матрицы

Учитывая сказанное, создадим программный модуль, который позволяет проверить наши возможности управления последовательностью valarray на примере задачи, близкой к реальности. Самым сложным моментом в реализуемом плане является создание функции f (), с помощью которой генерируется матрица заданной структуры, но произвольной размерности п. При генерации она помещается в последовательность типа valarray. Вторая функция (f и) проста. С ее помощью вычисляются коэффициенты уже известного вектора решений 1 :

//====== Глобально заданная размерность системы

int n;

//====== Граничные условия

double UO, UN;

//====== Функция вычисления коэффициентов

//====== трехдиагональной матрицы

double f ()

{

//====== Разовые начальные установки

static int raw = -1, k = -1, col = 0;

//====== Сдвигаемся по столбцам

col++;

//====== k считает все элементы

//====== Если начинается новая строка

if (++k % n == 0)

{

col =0; // Обнуляем столбец

raw++; // Сдвигаемся по строкам

}

//====== Выделяем три диагонали

return col==raw ? -2.

: col == raw-1 И col==raw+l ? 1 .

: 0.;

}

double fu()

{

//==== Вычисления вектора правых частей по формуле (5)

static double

dU = (UN-UO)/(n+1),

d = U0; return d += dU;

}

В функции main создается valarray с трехдиагональной матрицей и производится умножение матрицы на вектор решений. Алгоритм умножения использует сечение, которое вырезает из valarray текущую строку матрицы:

void main()

{

//======= Размерность задачи и граничные условия

n =4;

UO = 100.;

UN = 0 . ;

//======= Размерность valarray (вся матрица)

int nn = n*n;

//======= Матрица и два вектора

valarray<double> a(nn), u(n), v(n);

//======= Генерируем их значения

generate (&а[0], &a[nn], f); generate (&u[0], &u[n], fu);

out ("Initial matrix", a); out ("Initial vector", u);

//======= Умножение матрицы на вектор

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

//======= Выбираем i-ю строку матрицы

valarray<double> s = a[slice (i*n, n , 1)];

//======= Умножаем на вектор решений

//======= Ответ помещаем в вектор v <

transform(&s[0], &s[n], &u[0], &v[0], multiplies<double>());

//======= Суммируем вектор, получая

//======= i-ый компонент вектора правых частей

cout « "\nb[" « i « "] = " « v.sum(); }

cout «"\n\n";

}

Так как мы знаем структуру вектора правых частей, то, изменяя граничные условия и порядок системы, можем проверить достигнутую технику работы с сечениями.

Тестирование обнаруживает появление численных погрешностей (в пределах Ю" 15 ), обусловленных ограниченностью разрядной сетки, в случаях когда диапазон изменения искомой величины не кратен размеру расчетной области. Стоит отметить, что сечения хоть и являются непривычным инструментом, для которого хочется найти наилучшее применение, но в рамках нашей задачи вполне можно обойтись и без него. Например, алгоритм умножения матрицы на вектор можно выполнить таким образом:

for ( int i=0, id=0; i<n; i++, id+*n)

{

transform(&a[id], &a[id+n], &u[0], &v[0],

multiplies<dovible> () ) ;

cout « "\nb[" « i « "] = " « v.sum();

}

Эффективность этой реализации, несомненно, выше, так как здесь на один valarray меньше. Я думаю, что вы, дорогой читатель, сами найдете достойное применение сечениям.