Изучение программирования на конкретном алгоритмиче ском языке требует не только освоения его конструкций, но и знакомства с принципами разработки достаточно больших программ. В предыдущих главах, посвященных синтаксису и семантике языка Си, мы в качестве иллюстраций использо вали тексты небольших программ. В этой главе мы поместили более крупные программы, изучение которых позволит полу чить достаточно полное представление об особенностях проек тирования, разработки и кодирования реальных программ на языке Си.
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
Рис. 8.2. Состав программы для оценки последовательности псевдослучайных чисел
Глава 8. Примеры разработки программ
387
Алгоритм получения псевдослучайных нормально распреде ленных чисел оформим в виде функции pseudorand( ) без пара метров, возвращающей целочисленные значения в диапазоне от О до 16383. Так как при получении очередного (/-го) псевдослу чайного значения Si необходимо использовать предыдущие значения последовательностей Xi-ltyi-i,Zi-hSi-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;
: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() вместо формальных параметров должны подставляться адреса тех объектов (переменных), в которые функция поместит результаты вычислений.
Введем функцию, реализующую вычисления по аналитиче скому выражению для кривой нормального (Гауссова) распре деления :
При известных значениях математического ожидания (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;