Главная страница

Шпаргалка по языку СИ. Конспект Лекций «программирование На Языке Высокого Уровня Си» П. Конспект лекций для студентов высших учебных заведений, обучающихся по специальности 230102. 65 Автоматизированные системы обработки информации и управления


Скачать 1.25 Mb.
НазваниеКонспект лекций для студентов высших учебных заведений, обучающихся по специальности 230102. 65 Автоматизированные системы обработки информации и управления
АнкорШпаргалка по языку СИ
Дата26.05.2022
Размер1.25 Mb.
Формат файлаpdf
Имя файлаКонспект Лекций «программирование На Языке Высокого Уровня Си» П.pdf
ТипКонспект лекций
#550544
страница14 из 15
1   ...   7   8   9   10   11   12   13   14   15
{
int n = 4;
printf("Введите количество дисков : ");
scanf("%d",&n);
printf("\nХаной %d дисками :\n", n);
hanoy(1, 3, n, 0);
getch();
// system ("pause");
}
Результат выполнения программы
Введите количество дисков : 4
Ханой с 4 дисками :
перемещение(1,2)
перемещение(1,3)
перемещение(2,3)
перемещение(1,2)
перемещение(3,1)
перемещение(3,2)
перемещение(1,2)
перемещение(1,3)
перемещение(2,3)
перемещение(2,1)
перемещение(3,1)
перемещение(2,3)
перемещение(1,2)
перемещение(1,3)
перемещение(2,3)
Пример 13. Функция решение кубического уравнения с действительными коэффициентами методом Виета-Кардано. Корни могут быть комплексными.
Кубическое уравнение записывается в виде:
x
3
+a*x
2
+b*x+c=0.
Для нахождения его корней, в случае действительных коэффициентов, вначале вычисляются:
Q = (a2-3b)/9
R = (2a3-9ab+27c)/54.
Далее, если R2 < Q3, то уравнение имеет три действительных корня, вычисляющихся по формулам (Виета):
е = acos(R/sqrt(Q3))/3,
x
1
= -2*sqrt(Q)cos(t)-a/3,
x
2
= -2*sqrt(Q)cos(t+(2*pi/3))-a/3,
x
3
= -2*sqrt(Q)cos(t-(2*pi/3))-a/3.
В том случае, когда R2 >= Q3, то действительных корней один (общий случай) или два (вырожденные случаи). Кроме действительного корня, имеется два комплексно-сопряженных. Для их нахождения вычисляются (формула
Кардано):
202

A = -sign(R)[|R|+sqrt(R2-Q3)]1/3,
B = Q/A при A != 0 или B = 0 при A = 0.
Действительный корень будет:
x
1
= (A+B)-a/3.
Комплексно-сопряженные корни:
x
2,3
= -(A+B)/2-a/3 + i*sqrt(3)*(A-B)/2
В том случае, когда A = B, то комплексно-сопряженные корни вырождаются в действительный:
x2=-A-a/3.
Формулы Кардано и Виета требуют применения специальных функций, и в том случае, когда требуется провести большую серию вычислений корней кубического уравнения с не слишком сильно меняющимися коэффициентами, более быстрым алгоритмом является использование метода Ньютона или других итерационных методов (с нахождением начального приближения по формулам
Кардано-Виета).
#include
#include
#include
#include
#include
#define M_PI (3.141592653589793)
#define M_2PI (2.*M_PI)
/*
int Cubic(double *x,double a,double b,double c);
Параметры:
a, b, c - коэффициенты
x - массив решения (size 3).
На выходе:
3 действительных корня -> затем x ими заполняется;
1 действительный + 2 комплексных -> x[0] - действительный, x[1]
действительная часть комплексных корней, x[2] - неотрицательная мнимая часть.
Результаты:
1 - 1 действительный + 2 комплексных;
2 - 1 действительный корень + мнимая часть комплексных корней, если 0
(т.е. 2 действительных корня).
3 - 3 действительных корня;
*/
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));
203

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 в
204
поддиагональной части, а диагональные элементы L считаются все равными 1
(без ограничения общности) и не выводятся.
Алгоритм Краута представляет из себя следующее:
1.
Положить L
ii
=1 i=0,...,N-1 (здесь ничего не производится, а только имеется в виду для дальнейших шагов;
2. Для каждого j=0,1,...,N-1 проделать:
1.
Для всех i=0,...,j вычислить Uij:
U
ij
=A
ij
- SUM(0<=k
ik
*U
ik
) (при i=0 сумму полагать нулем);
2.
Для всех i=j+1,...,N-1 вычислить:
L
ij
= A
ij
- SUM(0<=k
ik
*U
ik
))/U
ii
Обе процедуры выполняются до перехода к новому j.
Устойчивая работа алгоритма Краута возможно только при условии выбора ведущего элемента для каждого обращения к j из п.2 алгоритма. Выбор производится перед выполнением п.2 для очередного j перестановки необработанных строк матрицы так, чтобы строка, подставляемая на место j, содержала наибольший по модулю элемент в колонке j. Порядок перестановки необходимо запомнить, для чего достаточно использовать дополнительный вектор целых величин. Еще один вектор используется внутри алгоритма для масштабирования.
Алгоритм Краута имеет несколько приложений, одно из которых
− решение системы линейных уравнений (обратная подстановка с учетом порядка перестановки строк), другие

вычисление обратной матрицы и вычисление детерминанта. Подробнее вызовы функций, связанных с алгоритмом Краута и реализация деталей алгоритма находятся в комментариях к программе и в ней самой.
#include
#include
#include
#include
#include
#define TINY 1.e-30
/*
LU-декомпозиция в соответствии с алгоритмом Краута.
Описание:
205

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;
206

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);
}
/* обратная замена */
void LU_backsub (double **a, int n, int *indx, double *b)
{
register int i, j, ip, ii = -1;
double sum;
/* первый шаг обратной замены */
for (i = 0; i < n; i++)
{
ip = indx[i];
sum = b[ip];
b[ip] = b[i];
if (ii >= 0)
for (j = ii; j < i; j++) sum -= a[i][j] * b[j];
207

else if (sum) ii = i; /* получен ненулевой элемент */
b[i] = sum;
}
/* второй шаг */
for (i = n - 1; i >= 0; i--)
{
sum = b[i];
for (j = i + 1; j < n; j++) sum -= a[i][j] * b[j];
b[i] = sum / a[i][i];
}
}
/* инвертирование матрицы */
void LU_invert (double **a, int n, int *indx, double **inv, double *col)
{
register int i, j;
for (j = 0; j < n; j++)
{
for (i = 0; i < n; i++) col[i] = 0.;
col[j] = 1.;
LU_backsub (a, n, indx, col);
for (i = 0; i < n; i++) inv[i][j] = col[i];
}
}
/* определяющее вычисление*/
double LU_determ (double **a, int n, int *indx, int *d)
{
register int j;
double res = (double)(*d);
for (j = 0; j < n; j++) res *= a[j][j];
return (res);
}
Пример 15. Алгоритм поиска интервала нахождения корня нелинейной функции.
Выяснение интервала, на котором корень содержится является важной проблемой поиска корня нелинейной функции действительной переменной.
Здесь приведен алгоритм поиска такого интервала и ограничения на его применение. Примем, что корень функции f(x) окружен на интервале [a,b], если f(a) и f(b) имеют противоположные знаки. Чтобы окруженный согласно этому определению корень действительно существовал на этом интервале, достаточно непрерывности f(x), а для его единственности

еще и монотонности. При невыполнении этих свойств возможно отсутствие корня на [a,b] или неопределенность его позиции.
При использовании компьютера, всегда имеем дело с дискретным набором возможных представлений чисел (хотя и достаточно плотным). Кроме того, монотонность вычисленной функции может быть слегка нарушена в пределах
208
точности ее вычисления. Это в ряде случаев усложняет вычисление окруженных корней функции, если к их точности предъявляются завышенные требования.
Окружение корня функции при гарантии ее определения на неограниченном интервале, производится по следующему итерационному алгоритму.
1.
Для начального приближения x
0
, найти f
0
=f(x
0
), задать начальный интервал поиска D и его инкремент d>1.
2.
Вычислить a=x
0
-D, b=x
0
+D; f a
=f(a), f b
=f(b).
3.
Увеличить интервал поиска: D=D*d. Если интервал превысил некоторый заданный предел, то выйти с индикацией ошибки.
3.a.
Если знаки f a
и f
0
отличаются, то считать корень окруженным на [a,x
0
]

> выход.
3.b.
Если знаки f b
и f
0
отличаются, то считать корень окруженным на [x
0
,b] -> выход.
3.c.
Если f
0
> 0 (случай меньше нуля делается аналогично) алгоритм продолжается:
4.
Проверяется, какое из f a
или f b
наименьшее. Если оба одинаковы, то переходим к 4a (двусторонний поиск), если f b

производим поиск вправо 4b, иначе

поиск влево 4c.
4.a.
Находим a=a-D, b=b+D, f a
=f(a), f b
=f(b), идем к пункту 3.
4.b.
Присваиваем последовательно a=x
0
, x
0
=b, f a
=f
0
, f
0
=f b
; находим b=b+D, f b
=f(b), идем к пункту 3.
4.c.
Аналогично 4b, только направление поиска

влево.
Так как интервал поиска постоянно расширяется, то в конце концов используя указанный алгоритм корень будет окружен. Возможны модификации алгоритма в двух направлениях:
1.
Увеличивать интервал не в геометрической прогрессии, а в арифметической либо по заданному сценарию;
209

2.
Если область определения функции заведомо ограничена, то расширение интервала поиска также следует ограничивать имеющимися пределами, либо доопределять функцию там, где ее оригинал не определен.
#include
#include
#include
#include
#include
/*
Предполагается, что функция существует на бесконечном и непрерывном промежутке.
int BracketRoot (double x0, double *a, double *b, double d0, double di,
double dmax, double (*fun)(double));
Параметры:
x0 - начальное приближение;
a - левая граница;
b - правая граница;
d0 - начальный интервал поиска;
di - интервал расширения (в геометрической прогрессии);
dmax - максимальный интервал;
fun - указатель на функцию.
Возвращает:
1 - если корень окружен;
0 - сбой
*/
int BracketRoot (double x0, double *a, double *b, double d0,
double di, double dmax, double (*fun)(double))
{
double fa, fb, f0;
f0 = (*fun)(x0);
*a = x0 - d0;
*b = x0 + d0;
fa = (*fun)(*a);
fb = (*fun)(*b);
while ((d0 *= di) < dmax)
{
if (f0 >= 0.)
{
if (fa < 0.)
{
*b = x0;
return (1);
}
if (fb < 0.)
{
*a = x0;
return (1);
}
if (fa > fb)
{
*a = x0;
x0 = (*b);
*b += d0;
fa = f0;
f0 = fb;
210

fb = (*fun)(*b);
}
else
if (fa < fb)
{
*b = x0;
x0 = (*a);
*a -= d0;
fb = f0;
f0 = fa;
fa = (*fun)(*a);
}
else
{
*a -= d0;
*b += d0;
fa = (*fun)(*a);
fb = (*fun)(*b);
}
}
else
if (f0 < 0.)
{
if (fa >= 0.)
{
*b = x0;
return (1);
}
else
if (fb >= 0.)
{
*a = x0;
return (1);
}
if (fa < fb)
{
*a = x0;
x0 = (*b);
*b += d0;
fa = f0;
f0 = fb;
fb = (*fun)(*b);
}
else
if (fa > fb)
{
*b = x0;
x0 = (*a);
*a -= d0;
fb = f0;
f0 = fa;
fa = (*fun)(*a);
}
else
{
*a -= d0;
*b += d0;
fa = (*fun)(*a);
fb = (*fun)(*b);
}
}
}
return (0);
}
211

Пример 16. Алгоритм нахождения Полинома Лагранжа (интерполяция
Лагранжа).
Интерполяционный многочлеен Лагранжа

многочлен минимальной степени, принимающий данные значения в данном наборе точек. Для n + 1 пар чисел (x_0, y_0), (x_1, y_1) \ dots (x_n, y_n), где все x i
различны, существует единственный многочлен L(x) степени не более n, для которого L(x i
) = y i
В простейшем случае (n = 1)

это линейный многочлен, график которого
− прямая, проходящая через две заданные точки.
1   ...   7   8   9   10   11   12   13   14   15


написать администратору сайта