Формирование
матрицы
Учитывая сказанное,
создадим программный модуль, который позволяет проверить наши возможности управления
последовательностью 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 меньше. Я думаю, что вы, дорогой читатель, сами найдете достойное применение сечениям.