Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

Глава 8 ПРИМЕРЫ РАЗРАБОТКИ ПРОГРАММ

Изучение программирования на конкретном алгоритмиче­ ском языке требует не только освоения его конструкций, но и знакомства с принципами разработки достаточно больших программ. В предыдущих главах, посвященных синтаксису и семантике языка Си, мы в качестве иллюстраций использо­ вали тексты небольших программ. В этой главе мы поместили более крупные программы, изучение которых позволит полу­ чить достаточно полное представление об особенностях проек­ тирования, разработки и кодирования реальных программ на языке Си.

8.1. П рограм м а с объектам и разны х классов пам яти

Постановка задачи. В качестве иллюстрации возможностей применения в одной программе объектов разных классов памя­ ти рассмотрим следующую достаточно.реальную задачу.

Пусть задан следующий способ получения равномерно рас­ пределенных псевдослучайных чисел (Gruenberger F., Babcock D. Computing with minicomputers. Los Angeles, 1973) :

 

ctj = (J(j +}>j +2 j +ctj_| j mod 16384,

Где Xj

= (2Xj-i) mod 16381;

у j

= (2jVj-j) mod 16363;

Zj

= (2zj-i) mod 16349.

Для j >

0 и aQ=0=y0 =z0=1•

В приведенных соотношениях mod - обозначение деления по модулю. В языке Си деление по модулю целых чисел реализует­ ся с помощью операции'%'.

Глава 8. Примеры разработки программ

383

Получаемые значения aj распределены достаточно равно­ мерно в диапазоне от 0 до 16383. Известно, что для получения величин, распределенных по нормальному закону из равномер­ но распределенных величин, можно использовать, например, такое приближенное соотношение :

/.+12

где L = 1, 13,25,...

Используя приведенные соотношения, нужно получить за­

данное количество N псевдослучайных чисел S . , i = \, N и

оценить для полученной совокупности выборочное среднее (математическое ожидание) т и выборочную дисперсию V :

Введя или определив предельные значения элементов Smm, Smax в выборке {.Si} из N элементов, сформировать гистограмму с заданным количеством К интервалов равной ширины.

Сравнить гистограмму, построенную для выборки псевдо­ случайных чисел, с кривой нормального (Гауссова) распределе­ ния, которая описывается уравнением:

где х - аргумент; V - дисперсия; т - математическое ожидание.

Гистограмма характеризуется следующими данными: Sm±n и Smax ~ предельные значения псевдослучайных чисел; N - общее

384

Программирование на языке Си

количество помещаемых в гистограмму значений; К - количест­ во равных интервалов разбиения оси абсцисс гистограммы; rij - количество значений Sj, относящихся к j -му интервалу, где

j = l K -

к

 

Естественно соотношение: N =

.

7=1

На основании анализа гистограммы можно по-другому и с меньшей точностью оценить среднее значение тд и дисперсию Vg исходной последовательности псевдослучайных чисел {Si}. Для пояснения рассмотрим изображение гистограммы на рис.8.1. По оси абсцисс гистограммы - получаемые значения Si псевдослучайных чисел. По оси ординат - количество rij значе­ ний Si, относящихся ку-му интервалу (сгруппированных в нем). Количество интервалов К гистограммы фиксировано, интервалы имеют равную ширину:

Рис. 8.1. Гистограмма для псевдослучайных чисел

Глава 8. Примеры разработки программ

385

Среднее значение абсциссы j-ro интервала :

c,=smjn+(y-i)*+|; j = \X

Cj представляет собой среднее значение данных (т.е. вели­ чин Si), отнесенных кУ-му интервалу гистограммы. Для каждо­ го у-го интервала количество значений S, равно и;, поэтому для оценки среднего значения mg выборки {S^} по данным гисто­ граммы применимо соотношение :

к

Оценить выборочное значение дисперсии по данным гисто­ граммы можно так :

2

Получаемое таким образом значение Vg оказывается немного больше оценки выборочной дисперсии V для всей выборки {Si}. Более точное значение оценки дисперсии dg получается с по­ мощью поправки Шеппарда (равной Л/12), т .е .:

Здесь h - ширина интервала гистограммы.

Программная реализация. Рассмотрим основные принципы построения программы для решения поставленной задачи.

Программу построим в виде набора функций, не обязательно размещенных в одном файле (рис. 8.2). Связи между функциями реализуем не только с помощью параметров, но и с применени­ ем внешних объектов (указателей, массивов, переменных), ко-

25 -3 1 2 4

386

Программирование на языке Си

торые нужно сделать' доступными в тех функциях, где к ним потребуется обращаться.

Статические внешние объекты N К smin gisto smax sum sum2

Память для К элементов гистограммы

void main ()

{ void count(void);

void estimate (double *, double *, double *); void compare(double, double);

}

void count (void)

{

int pseudorand(void);

}

int pseudorand(void)

{

static unsigned x, y, z, s;

void estimate(double *mg, double *vg, double *dg)

{

}

void compare (double mat, double dis)

{

double gauss(double, double, double);

double gauss (double x, double m, double v)'

{

}

}

Рис. 8.2. Состав программы для оценки последовательности псевдослучайных чисел

Глава 8. Примеры разработки программ

387

Алгоритм получения псевдослучайных нормально распреде­ ленных чисел оформим в виде функции pseudorand( ) без пара­ метров, возвращающей целочисленные значения в диапазоне от О до 16383. Так как при получении очередного (/-го) псевдослу­ чайного значения Si необходимо использовать предыдущие значения последовательностей Xi-lt yi-i, Zi-h Si-i, то в теле функции предусмотрим их сохранение в статических (static) переменных. Предлагаемый вариант функции:

int pseudorand(void)

{

static unsigned int x=l, y=l, z=l, s=l; int i ;

long sm = 0;

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

{

x=(2*x)%16381;

y=(2*y)%16363;

z=(2*z)%16349;

s=(x+y+z+s)%16384;

sm+=s;

}

return sm/12;

}

Разработаем функцию count( ) для "заполнения" К элементов массива гистограммы с одновременным вычислением сумм

N

N

 

» S5.2-

(=1

/=1

И с х о д ны е д анн ые для

ф у н к ц ии c ou nt ( ):

N - количество формируемых псевдослучайных чисел; К - число интервалов гистограммы;

smin - минимально допустимое значение анализируемых псевдослучайных чисел;

smax - максимально допустимое значение анализируемых псевдослучайных чисел.

25*

388

Программирование на языке Си

Р е з у л ь т а т ы ,

ф о р м и р у е м ы е ф ун к ц и е й count( ):

gisto[/Q - массив значений tij количества псевдослучайных чисел Sj, сгруппированных в К интервалов гистограммы;

sum - сумма всех N анализируемых псевдослучайных чисел

Sj, i = , N \

sum2 - сумма квадратов всех N анализируемых псевдослу­ чайных чисел.

Как исходные данные функции count(), так и результаты ее выполнения нужны в следующей функции estimate(), назначе­ ние которой - собственно вычисление оценок средних и дис­ персий анализируемой совокупности из N псевдослучайных чисел. Исходя из этого определим переменные N, К, smin, smax, sum, sum2 как внешние объекты, а участок памяти для гисто­ граммы будем формировать динамически по известному значе­ нию К перед обращением к функции count( ). Для связи с этим участком памяти определим внешний указатель int * gisto.

Т ек с т ф ун к ц и и :

void count (void)

{/* Формирование гистограммы и вычисление сумм */ extern long N;

extern int К; extern int smin; extern int smax; extern int *gisto; extern double sum; extern double sum2;

int pseudorand(void); /* Прототип */ double h; /* Интервал гистограммы */

int j , i ; double d;

:sum = sum2 = 0.0; h = (smax-smin)/К; for (i=0; i<N; i++)

{d = pseudorand( );

j = (int)((d-smin)/h); if (j<K && j>=0)

/* Заполнение интервала гистограммы */

gisto[j]++;

Глава 8. Примеры разработки программ

389

else

printf("\nj=%d d=%8.Of", j , d); sum+=d;

sum2+=d*d;

>

}

На основе данных гистограммы следующая функция estimate() будет вычислять оценку математического ожидания mg и две оценки дисперсии vg и dg (последняя с учетом поправ­ ки Шеппарда).

И с х о д н ы е

д а н н ые для ф ун к ц и и e s t i m a t e ( ):

К - число интервалов гистограммы;

gisto[/Q -

массив значений в интервалах гистограммы

(доступен в функции с помощью внешнего указателя gisto); smin, smax - пределы гистограммы (по абсциссе);

N - количество анализируемых псевдослучайных чисел, по­ мещенных в гистограмму.

Р е з у л ь та т ы , ф о р м и р у е м ы е ф у н к ц и е й e s ti m a t e ( ): mg - оценка математического ожидания по данным из гисто­

граммы (double *);

vg - оценка дисперсии по гистограмме (double *);

dg - уточненная оценка дисперсии по гистограмме (double *).

Исходные Данные передаются в функцию estimate() через внешние переменные. Для передачи результатов из функции estimate() будем использовать аппарат параметров. Как неодно­ кратно подчеркивалось, это потребует работы с указателями и адресами, так как параметры в функциях на языке Си переда­ ются только по значениям.

void estimate(double *mg, double *vg, double *dg)

{

extern

int K;

extern

int

* gisto;

extern

long N;

extern

int

smin;

extern

int

smax;

int j ;

 

 

390

 

Программирование на языке Си

double-h, сj , с;

double sm=0.0,

sm2—0 .0;

h =

(smax-smin)/К;

for

( j=0; j<K;

j++)

{ oj

= smin + j*h + h/2;

о =* gisto[j]

* cj ;

sm+=c;

 

sm2+=c*cj ;

}

*mg=sm/N; *vg=(sm2-sm*sm/N)/ (N-l); *dg=*vg-h/12;

}

Обратите внимание на операцию разыменования, применяе­ мую к параметрам-указателям mg, vg, dg. При обращении к функции estimate() вместо формальных параметров должны подставляться адреса тех объектов (переменных), в которые функция поместит результаты вычислений.

Введем функцию, реализующую вычисления по аналитиче­ скому выражению для кривой нормального (Гауссова) распре­ деления :

#include <math.h>

double gauss (double x, double m, double v)

{

const double PI=3.14159265358979; double z, e ;

z=sqrt(2*PI*v); e=(x-m)* (x-m)/ (2*v); return exp(-e)/z;

}

При известных значениях математического ожидания (m) и дисперсии (v) по заданному значению аргумента (х) функция вычисляет точку кривой нормального распределения.

Функция gauss() используется в следующей функции compare(), где выполняется сравнение сформированной гисто­ граммы с теоретической гауссовой кривой. Функция compare() печатает (выводит на экран) таблицу со значениями из гисто­ граммы и теоретической кривой. Теоретическая кривая строится

Глава 8. Примеры разработки программ

391

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

void compare (double mat, double dis)

{/* Сравнение гистограммы с кривой Гаусса */ double h , сj, dj, cko = 0, гаг;

extern int * gisto; extern int K; extern int smin; extern int smax;

double gauss(double,gauss,double);/*Прототип*/ long nn = 0;

int j ;

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

.nn+=gisto[j];

h=(smax-smin)/К; printf("\пСравнительная таблица\п");

printf("

j

| cj

|

gisto[j]

|"

"

gauss(cj)

|

delta\n");

 

for (j=0; j<K;

j++)

 

 

 

{

cj

=

smin + j*h + h/2;

 

dj

=

gauss(cj,mat,dis)*nn*h;

% 12.le\n",

printf("%2d

%7.If %d %7.Of

 

 

j, cj,

gisto[j], dj,

gisto[j]-dj);

raz = gisto[j]-dj; cko+=raz * raz;

}

cko = sqrt(cko)/K; printf("\ncko»%f\n", cko);

}

Кроме печати таблицы, в цикле вычисляется среднее квадра­ тическое отклонение данных гистограммы от теоретических значений гауссовой кривой:

где п, - значение ординаты гистограммы; _У/ - теоретическое значение, полученное для дс=С/ (/'=/, К).

Соседние файлы в папке книги