Учебная работа. Курсовая работа: Численные методы при решении задач
Тема: «Численные способы при решении задач»
Создатель: студент группы ПС-146
Проверил:
Оглавление
Оглавление. 2
Программки и описания. 3
программка для решения задачки 17. 3
Условие задачки 17.3
Решение задачки по способу Адамса. 3
Блок-схема функции main из программки 17.c. 4
Блок-схема функции Adams из программки 17.c. 5
Листинг программки 17.c. 6
Итог решения задачки 17 на ЭВМ .. 9
Вывод:9
Программка для решения задачки 30.10
Условие задачки 30.10
Решение задачки по способу меньших квадратов. 10
Блок-схема функции main из программки 30.c. 11
Блок-схема функции MMinor из программки 30.c. 11
Блок-схема функции MatrixMultiply из программки 30.c. 12
Блок-схема функции Determinant из программки 30.c. 12
Листинг программки 30.c. 12
Итог решения задачки 30 на ЭВМ .. 17
Вывод:17
Программки и описания
программка для решения задачки 17
Условие задачки 17.
Создать функцию численного интегрирования системы дифференциальных уравнений способом Адамса. Макет функции:
где:
– Функция вычисления правых частей системы дифференциальных уравнений:
– Массив размера
значений зависимых переменных;
– Массив размера
значений зависимых производных;
– порядок системы дифференциальных уравнений;
– Независящая переменная;
– Изначальное
– Конечное
– Изначальное число разбиений отрезка интегрирования [
;
]
– относительная погрешность интегрирования. Вычисления прекращаются, когда , где – i
-й составляющие вектора зависимых переменных при
для количества разбиений отрезка интегрирования
.
Исходные шаги делаются по способу Рунге-Кутта.
Применить эту функцию для интегрирования дифференциального уравнения 3-его порядка y(3)
+2
x2
в интервале xÎ
с шагом
, и исходными критериями
.
Решение задачки по способу Адамса
Для пуска экстраполяционного способа Адамса требуется 4 исходных значения функции. Одно значение уже задано, а другие получаются по способу Рунге-Кутта 4 порядка. Опосля вычисления значения в конце отрезка происходит вычисление относительной погрешности (из текущих и ранее приобретенных с шагом h значений функции) и сопоставление её с данным значением. Если приобретенная погрешность меньше, чем данная, то считается, что задачка выполнена и происходит возврат в вызывающую программку с приобретенным значением функции. Если же нет – то миниатюризируется в 2 раза шаг и весь процесс, начиная с способа Рунге-Кутта, повторяется вновь (для вычисления новейших значений функции). Так длится до того времени, пока приобретенное
Для работы программки нужна функция вычисления правых частей системы дифференциальных уравнений. Это функция
. Т. к. в задачке требуется решить уравнение y(3)
+2
x2
,
составляем систему дифференциальных уравнений первого порядка. Смотрится она так:
При любом вычислении левых частей данной системы происходит дифференцирование
и
, т. е. вычисление соответственно новейших значений
.
Ну, а если переложить это всё в программку на Си, то получится функция
(смотри листинг 17 задачки).
Блок-схема функции
mainиз программки 17.c
Блок-схема функции
Adamsиз программки 17.c
Листинг программки 17.
c
// задачка 17. Численное интегрирование системы дифференциальных уравнений
// способом Адамса. программка рассчитана на компиляцию в Micro$oftC 6.00
// либо Borland C 3.1+
// (C) 2004 REPNZ. All rights reserved. Release date is 2.04.2004
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void func (double *y, double *ys, double t)
{ // функция вычисления правых частей уравнений
ys[0] = y[1]; // ys[1]-первая производная; ys[2]-вторая и т.д.
ys[1] = y[2]; // t-независимый аргумент
ys[2] = 5 + t * t — y[0] — 3. * y[1] — 2. * y[2];
}
void Adams (
void f (double *y, double *ys, double x),
// Функция вычиления правых частей системы
double *y, // Массив размера n значений зависимых переменных
int n, // Массив размера n значений производных
double tn, // Начало интервала интегрирования
double tk, // Конец интервала интегрирования
int m, // Изначальное число разбиений отрезка интегрирования
double eps) // Относительная погрешность интегрирования
{
double *k1, *k2, *k3, *k4; // Для способа Рунге-Кутта
double *q0, *q1, *q2, *q3; //
double *ya; // Временный массив
double *y0, *y1, *y2, *y3; // значения функции для способа Адамса
double h; // Шаг интегрирования
doublexi; // Текущее
double eps2; // Для оценки погрешности
double dq2, dq1, dq0, d2q1, d2q0, d3q0; // приращения
int flag = 0; // 0, пока идет 1-ый просчёт
int i, j; // Индексы
if (m < 4) m = 4; // Минимум 4 отрезка
if (tn >= tk)
{ printf («nНеправильные аргументыn»);
abort (); // Некорректные аргументы
}
// Выделяем память для массивов с переменными
if ((k1 = malloc ((4 + 4 + 4 + 1) * n * sizeof (double))) == 0)
{ printf («nОшибка распределения памятиn»);
abort (); // Оборвать, если не удалось
}
// Распределяем память меж массивами:
// Для способа Рунге-Кутта 4 порядка
k2 = k1 + n; k3 = k2 + n; k4 = k3 + n;
// 4 пердыдущих значения функции
y0 = k4 + n; y1 = y0 + n; y2 = y1 + n; y3 = y2 + n;
// Для временного массива сбора данных
ya = y3 + n;
// Для способа Адамса
q0 = ya + n; q1 = q0 + n; q2 = q1 + n; q3 = q2 + n;
h = (tk — tn) / m; // Шаг
eps = fabs (eps); // Абсолютное
start: // Отсюда начинаются вычисления
xi = tn; // Начало промежутка
// Вычисляем значения функции y0…y3, т.е. y[i-3] … y[0]
// 1-ое значение системы уравнений уже дано: y …
///////////////////////////////////////////////////////////////////////
// — Способ Рунге-Кутта 4 порядка — //
///////////////////////////////////////////////////////////////////////
for (j = 0; j < n; j++) y0[j] = y[j]; // Копируем его в y0
f (y0, q0, xi); // Заполняем q0, основываясь на значениях из y0
for (j = 0; j < n; j++) q0[j] *= h; // Делаем q0
xi += h; // Последующий шаг
// … а другие 3 добываем при помощи способа Рунге-Кутта 4 порядка.
for (i = 0; i < 3; i++) // i — КАКОЕ
{ // А ВЫЧИСЛЯЕМ значения Y[i+1]!!!!
// Поначалу необходимы коэффициенты k1
// Элемент y[i, j] = y0 + (i * n) + j = y0[i * n + j]
f (&y0[i * n], k1, xi); // Вычислим f(xi, yi) = k1 / h
// И для всякого дифференциального уравнения системы проделываем
// операции вычисления k1, также подготовки в ya аргумента для
// вычисления k2
for (j = 0; j < n; j++)
{
k1[j] *= h; // Вычислим наконец k1
ya[j] = y0[i*n+j] + k1[j] / 2.;
// И один из аргументов для функции
} // вычисления k2
f (ya, k2, xi + (h / 2.)); // Вычислим f(xi,yi) = k2 / h
for (j = 0; j < n; j++)
{ // Вычислим наконец k2
k2[j] *= h;
ya[j] = y0[i*n+j] + k2[j] / 2.; // И один из аргументов для функции
} // вычисления k3
f (ya, k3, xi + h / 2.); // Вычислим f(xi,yi) = k3 / h
for (j = 0; j < n; j++)
{
k3[j] *= h; // Вычислим наконец k3
ya[j] = y0[i*n+j] + k3[j]; // И один из аргументов для функции
} // вычисления k4
f (ya, k4, xi + h); // Вычислим f(xi,yi) = k4 / h
for (j = 0; j < n; j++) k4[j] *= h; // Вычислим наконец k4
// нужно вычислить приращение каждой функции из n
for (j = 0; j < n; j++) // Вычисляем последующее
// функции
// Y[i+1] = Yi + …
y0[(i+1)*n+j] = y0[i*n+j] + (k1[j] + 2. * k2[j] + 2 * k3[j] + k4[j]) / 6.;
// И новое
f (&y0[(i+1)*n], &q0[(i+1)*n], xi); // qi = f (xi, yi);
for (j = 0; j < n; j++) q0[((i+1)*n)+j] *= h;
xi += h; // Последующий шаг }
///////////////////////////////////////////////////////////////////////
// — способ Адамса — //
///////////////////////////////////////////////////////////////////////
// Итак, вычислены 4 первых значения. Этого довольно для начала способа
// Адамса для шага h.
// B y0…y3 лежат 4 значения функций (_НЕ_ПРОИЗВОДНЫХ!!!).
// A в q0…q3 лежат значения _производных_ этих функций, умноженных на h
// q0..q3, также y0..y3 представляют собой очереди с 4 элементами
again: // Вычисляем новое
for (j = 0; j < n; j++)
{ // Все приращения
dq2 = q3[j] — q2[j]; dq1 = q2[j] — q1[j]; dq0 = q1[j] — q0[j];
d2q1 = dq2 — dq1; d2q0 = dq1 — dq0;
d3q0 = d2q1 — d2q0;
// новое
ya[j] = y3[j] + (q3[j] + (dq2 / 2.) + (5. * d2q1 / 12.) + (3. * d3q0 / 8.));
// Сдвигаем все массивы на 1 вперёд и добавляем в очередь новое
//
y0[j] = y1[j]; y1[j] = y2[j]; y2[j] = y3[j]; y3[j] = ya[j];
// Просто сдвигаем q, ничего пока что не добавляя
q0[j] = q1[j]; q1[j] = q2[j]; q2[j] = q3[j];
}
// В очередь в качестве q3 ложим новое
f (y3, q3, xi); // q3 = f (xi, y3);
for (j = 0; j < n; j++) q3[j] *= h; // Вычислить q3
// Еще одно
xi += h;
// Продолжить интегрирование?
if (xi < tk) goto again; // Да.
// Если 1-ый раз тут, то просчитать ещё раз с шагом h/2
if (flag == 0)
flag = 1; // Ассоциировать уже будет с чем
else
{
// Не 1-ый раз — оценить погрешность
// на данный момент в y3 — значение лишь что вычисленной функции ,
// а в y2 — занчение функции, вычисленной с шагом h * 2
// по отношению к текущему
for (j = 0; j < n; j++)
{ eps2 = fabs (((y3[j] — y2[j]) / y2[j]));
if (eps2 > eps) break; // Если погрешность очень великА
}
if (j == n) // Если всё ОК
{ // Копируем итог
for (j = 0; j < n; j++) y[j] = y3[j];
free (k1); // Освобождаем память
return; // Возвращаемся в main
}
}
// По каким-то причинам выхода из функции не вышло —
// тогда уменьшаем шаг в 2 раза и повторяем
// всё, начиная с способа Рунге-Кутта
h /= 2.; // Уменьшить шаг
goto start; // Повторить расчёт поначалу, с новенькими параметрами
}
int main ()
{
double y[3], xs, xe;
int i;
y[0] = 1.; y[1] = 0.1; y[2] = 0.; // Исходные условия
xs = .0; xe = .1; // Начало интегрирования
printf («x = %5.3lg, y(%4.2lg) = %10.3lgn», xs, xs, y[0]);
for (i = 0; i < 20; i++)
{
Adams (func, y, 3, xs, xe, 10, 1.e-3);
xs += 0.1; xe += 0.1;
printf («x = %5.3lg, y(%4.2lg) = %10.3lgn», xs, xs, y[0]);
}
return 0;
}
Итог решения задачки 17 на ЭВМ
Для работы программку нужно скомпилировать в модели не ниже SMALL. Употреблялся компилятор Micro$oftC 6.00 из одноимённого пакета. Опосля пуска программка выводит последующее:
Программка численного интегрирования системы дифференциальных
уравнений экстраполяционным способом Адамса
Разраб: студент гр. ПС-146
Нечаев Леонид Владимирович
17.03.2004
Дифференциальное уравнение имеет вид y»’ + 2y» + 3y’ + y = x^2 + 5
Итак, зависимость y[x]:
x = 0, y( 0) = 1
x = 0.1, y(0.1) = 1.01
x = 0.2, y(0.2) = 1.02
x = 0.3, y(0.3) = 1.04
x = 0.4, y(0.4) = 1.07
x = 0.5, y(0.5) = 1.11
x = 0.6, y(0.6) = 1.16
x = 0.7, y(0.7) = 1.22
x = 0.8, y(0.8) = 1.28
x = 0.9, y(0.9) = 1.37
x = 1, y( 1) = 1.46
x = 1.1, y(1.1) = 1.56
x = 1.2, y(1.2) = 1.67
x = 1.3, y(1.3) = 1.79
x = 1.4, y(1.4) = 1.92
x = 1.5, y(1.5) = 2.06
x = 1.6, y(1.6) = 2.21
x = 1.7, y(1.7) = 2.36
x = 1.8, y(1.8) = 2.52
x = 1.9, y(1.9) = 2.69
x = 2, y( 2) = 2.86
Вывод:
Проверяем решение в программке Mathematica 4.2. Результаты, приобретенные с точностью до 2 символов опосля запятой не различаются от приобретенных. задачка решена правильно.
Программка для решения задачки 30.
Условие задачки 30.
Создать программку аппроксимации функции способом меньших квадратов для модели по таблице результатов опыта:
X1
X2
Y
1
1
0
-1
-1
-2
2
2
-2
3
-2
29
-2
4
54
Решение задачки по способу меньших квадратов
Рассчитываемая модель линейна относительно собственных коэффициентов ai
. Задана матрицы и , также функция для получения матрицы F. F– Особая матрица, которая рассчитывается по методу, приведённому ниже. Функция представляет собой мою свою разработку, но полностью может быть её вводить вручную. метод составления матрицы F(беря во внимание разложение ):
, где — функции из модели y, а .- n-й элемент матрицы .
Исходя из этих формул строится функция f (смотри листинг программки 30.c).
Дальше, по формуле находится матрица с коэффициентами ai
и выводится на экран.
Блок-схема функции
mainиз программки 30.c
Нет
Блок-схема функции
MMinorиз программки 30.c
Блок-схема функции
MatrixMultiplyиз программки 30.c
Блок-схема функции
Determinantиз программки 30.c
Листинг программки
30.c
// задачка 30. Аппроксимация функции способом меньших квадратов
// (C) 2004 REPNZ
// Включаемые файлы
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <stdlib.h>
// ————— Описание исходных значений ——————
// Дано (размеры матриц — (1 х высота):
// xm — это матрицы-столбецы независящих переменных
// xm = (x1, x2, … xN)T высотой xr
// Вектор наблюдений. ym — его матрица:
// ym = (y1, y2, …, yM)T высотой yr
// Также описания функций при коэффициентах a1, a2, …, aK
// 1. Матрицы с элементами типа double
// — количество частей в столбцевых маритцах xm и ym
#define xr 2
#define yr 5
// — Данные значения х
static double xm1[xr] = {1, 1};
static double xm2[xr] = {-1, -1};
static double xm3[xr] = {2, 2};
static double xm4[xr] = {3, -2};
static double xm5[xr] = {-2, 4};
// — Массив указателей на эти значения
static double *xmp[yr] = {xm1, xm2, xm3, xm4, xm5};
// — Матрица со значениями функции
static double ym[yr] = {0, -2, -2, 29, 54};
// 2. Функции из модели
// — сколько их
#define n 3
// И фактически сами функции, записываются как тело Си-функции
double f(double xm[xr], int path)
// — какие конкретно (n штук путей, выбирается параметром Path)
{
switch (path)
{
// Функция 1
case 1:
return xm[0]; // x1
// Функция 2
case 2:
return xm[1]*xm[1]; // x2^2
// Функция 3
case 3:
return xm[0]*xm[1]; // x1*x2
}
printf («nНеправильная функцияn»);
abort ();
}
// Ну и модель соответственно вышла: y = a1 * x1 + a2 * x2^2 + a3 * x1 * x2
char txtmodel[] = «y = a1x1 + a2x2^2 + a3x1x2»;
// Короче, n = K, xr = N, yr = M (!) 😉
///////////////////////////////////////////////////////////////////////////////
// =-=-=-=-=-=-=-=-=-=-=-=-=-= Функции и подпрограммы =-=-=-=-=-=-=-=-=-=-=-=-=
///////////////////////////////////////////////////////////////////////////////
// Печать матрицы m. размеры (x * y)
void mprint (double *m, int x, int y)
{
int i, j; // Индексы для прохода
for (j = 0; j < y; j++) // По строчкам
{
for (i = 0; i < x; i++) // По элементам строчки
{ // Элемент
printf («%8.4lg «, *(m + (j * x + i)));
}
printf («n»); // CR/LF
}
}
///////////////////////////////////////////////////////////////////////////////
// Перемножение матриц m1 (размер — rows1 * cols1) и m2 (размер — cols1 * cols2)
// Итог помещается в result
void MatrixMultiply (double *m1, int rows1, int cols1, double *m2, int cols2, double *result)
{
int i, j, k;
// Получится матрица высотой rows1 и длиной cols2
for (j = 0; j < rows1; j++) // Проход по высоте
{
for (i = 0; i < cols2; i++) // Проход по длине
{
// Чистка элемента
*(result + (cols2 * j + i)) = 0;
for (k = 0; k < cols1; k++) // Проход по элементам
// строчки первой матрицы
// Вычисление еще одного элемента результата
*(result + (cols2 * j + i)) +=
*(m1 + (cols1 * j + k)) * (*(m2 + (cols2 * k + i)));
}
}
}
///////////////////////////////////////////////////////////////////////////////
// Вычисляет минор матрицы m, приобретенный вычёркиванием элемента (xel; yel)
// и ложит его в res
void MMinor (double *m, double *res, int siz, int xel, int yel)
{
int i, j, ki = 0, kj = 0; // Начальное состояние
for (j = 0; j < (siz — 1); j++) // Проходим по строчкам матрицы res
{
if (j == yel) kj = 1; // Пропустить текущую строчку
for (i = 0; i < (siz — 1); i++)// Проходим по столбцам матрицы res
{
if (i == xel) ki = 1; // Пропустить текущий столбец
*(res + j * (siz — 1) + i) = *(m + (j+kj) * siz + (i+ki));
}
ki = 0; // Для последующей строки (yel строчку уже пропустили)
}
}
///////////////////////////////////////////////////////////////////////////////
// Вычисление определителя матрицы m размером (dim * dim)
// (Рекурсивная функция)
double Determinant (double *m, int dim)
{
// Все переменные — ОБЯЗАТЕЛЬНО ЛОКАЛЬНЫЕ!!!
doubled = 0, k = 1; // Определитель и флаг
int ki, kj, di, dj, i; // Коэффициенты, индексы, смещения
double *mm; // Новенькая матрица с вычеркнутой строчкой и столбцом
if (dim < 1) { printf («nНеправильные аргументы»); abort (); }
if (dim == 1) return *m; // Если матрица 1х1
// Выделяем память для минора
if ((mm = malloc ((dim — 1) * (dim — 1) * sizeof (double))) == 0)
{ printf («nОшибка распределения памятиn»); abort (); }
// Если матрица 2х2
if (dim == 2) d = ((*m) * (*(m + 3)) — (*(m + 2) * (*(m + 1))));
else // Размер больше чем 2
// Раскладываем матрицу по нулевой строке
for (i = 0; i < dim; i++)
{
MMinor (m, mm, dim, i, 0); // Вычеркнем столбец и
// строчку в матрицк
d += k * (*(m + i)) * Determinant (mm, (dim — 1));
k = 0 — k;
}
free (mm); // Высвободить память под минор
returnd; // Возвратить
}
///////////////////////////////////////////////////////////////////////////////
// Основная часть програмыы
int main (void)
{
// Аппроксимация функции для модели y
double *F; // Особая матрица F n*y
double *TF; // Транспонированная F y*n
double *REV; // Оборотная матрица n*n
double *TMP; // Временная матрица n*n
double *AC2; // Алгебраические дополнения (n-1)*(n-1)
double dt; //
double flag; // Флаг для оборотной матрицы
int i, j; // Индексы
// Представим программку юзеру 🙂
printf («nПрограмма аппроксимации функции способом меньших квадратов для»
» моделиn %s»
«nпо данной таблице опыта.»
«nn Разраб: студент группы ПС-146»
«n Нечаев Леонид Владимирович»
«n 25.02.2004»
, txtmodel);
printf («nИзвестны результаты наблюдений:»
«n x1 x2 y»);
for (i = 0; i < yr; i++)
printf («n%10.4lg%8.4lg%8.4lg», *(xmp[i]), *(xmp[i] + 1), ym[i]);
printf («nНачинаем аппроксимацию…n»);
// Требуется посчитать am. Так:
// am — это матрица-столбец разыскиваемых коэффициентов. Представляет из себя
// am = (a1, a2, …, aK)T высотой n, а считается так:
// am = Inverse[Transpose[F].F].Transpose[F].ym, где
// F — мартица, составленная особым образом (смотри ниже):
// Выделяем памяти сходу на все матрицы — F, TF, REV, TMP, AC2
#define memneed (((n * yr) + (yr * n) + (n * n) + (n * n) + ((n-1) * (n-1))) * eof (double))
if ((F = malloc (memneed)) == 0)
{
printf («nОшибка распределения памяти. Поменяйте комп«);
abort(); // Если не удалось выделить для неё память
}
TF = F + (n * yr);
REV = TF + (yr * n);
TMP = REV + (n * n);
AC2 = TMP + (n * n);
// Наполнение значениями матрицы F
for (j = 0; j < yr; j++) // Цикл по строчкам F
{
for (i = 0; i < n; i++) // И по столбцам F
{
// Заполняем j-й строчка значениями функций fi
*(F + (j * n + i)) = f (xmp[j], (i + 1));
}
}
// Матрица F готова. нужно вычислить по формуле:
// am = Inverse[Transpose[F].F].Transpose[F].ym
// коэффициентов a1, a2, a3, …
// Транспонируем F
for (j = 0; j < n; j++) // Цикл по строчкам TF
{
for (i = 0; i < yr; i++) // И по её столбцам
{
*(TF + (j * yr + i)) = *(F + (i * n + j));
}
}
// Считаем TMP = TF * F
MatrixMultiply (TF, n, yr, F, n, TMP);
// Дальше считаем оперделитель от TMP
if ((dt = Determinant (TMP, n)) == 0)
{
printf («nТак, как определитель матрицы TF*F равен 0,n»
«нереально посчитать оборотную к ним матрицуn»);
free (F); abort();
}
// Составляем оборотную матрицу.
for (j = 0; j < n; j++)
{
for (i = 0; i < n; i++)
{
// Берём Минор элемента ij
MMinor (TMP, AC2, n, i, j);
// символ элемента
flag = ((i + j) % 2 == 0) ? 1. : -1.;
// Сходу транспонирование
*(REV + (i * n) + j) = flag * Determinant (AC2, (n — 1)) / dt;
}
}
// Умножаем оборотную матрицу на транспонированную к F
// т.е. Inverse (TF*F) * TF
// Таковая матрица будет размера yr*n, потому полностью хватит памяти для F
MatrixMultiply (REV, n, n, TF, yr, F);
// И, в конце концов, всё это умножаем на матрицу Y и получаем разыскиваемые
// коэффициенты a1, a2, … aN
// Для таковой матрицы (размером 1*n) полностью хватит памяти под REV
MatrixMultiply (F, n, yr, ym, 1, REV);
// Всё, печатаем ответ
printf («nВычисления успешны, получен последующие коэффициенты:»);
for (i = 0; i < n; i++)
printf («na%d = %lg», i, *(REV + i));
// Высвободить память
free (F);
printf («nНажмите any key»);
getch ();
printf («nDone.n»);
return 0;
}
Итог решения задачки 30 на ЭВМ
Опосля пуска программка сходу же начинает расчёт коэффициентов. На экран выводится последующее:
программка аппроксимации функции способом меньших квадратов для модели
y = a1x1 + a2x2^2 + a3x1x2
по данной таблице опыта.
Разраб: студент группы ПС-146
Нечаев Леонид Владимирович
25.02.2004
Известны результаты наблюдений:
x1 x2 y
1 1 0
-1 -1 -2
2 2 -2
3 -2 29
-2 4 54
Начинаем аппроксимацию…
Вычисления успешны, получены последующие коэффициенты:
a0 = 1
a1 = 2
a2 = -3
Нажмите any key
Done.
Итог верен, потому что при подстановке переменных в модель выходит верное равенство:
0 = 1 * 1 + 2 * 1 – 3 * 1 * 1
-2 = 1 * (-1) + 2 * (-1) – 3 * (-1) * (-1)
-2 = 1 * 2 + 2 * 2 – 3 * 2 * 2
29 = 1 * 3 + 2 * (-2) – 3 * 3 * (-2)
54 = 1 * (-2) + 2 * 4 – 3 * (-2) * 4
Вывод:
задачка решена правильно.
]]>