Язык Си в примерах/Максимум: различия между версиями

Содержимое удалено Содержимое добавлено
м {{BookCat}}; избыточные <big /> и <font /> вокруг <source />; ссылки; пробелы.
Переписано.
Строка 1:
{{{{Book template}}/Содержание «Язык Си в примерах»}}
 
; {{Якорь2 |Дано}}: непустая последовательность разделенных ''пробельными символами'' [[w:Целое число |целых чисел]] (в «текстовом» представлении) на [[w:Стандартные потоки#Стандартный ввод |стандартном вводе]] программы. Последовательность конечна, но ее длина заранее неизвестна.
Задача: написать программу, которая находит максимум из введенных целых чисел. На ввод поступает количество чисел, а затем сами числа.
; Найти: предельные значения последовательности (минимум, максимум) и основные статистики (среднее, среднеквадратичное отклонение.)
 
== Решение ==
Вот решение этой задачи:
 
{{Якорь |intstats.c}}
<source lang="c">
#include <assert.h>
/* Программа max.c "Максимум чисел" */
#include <stdiomath.h>
#include <stdio.h>
int main () {
int i, n, a, max;
printf ("Введите количество чисел: ");
scanf ("%d", &n);
printf ("Введите %d чисел: ", n);
scanf ("%d", &max);
for(i = 1; i < n ; i++) {
scanf ("%d", &a);
if(a > max)
max = a;
}
printf ("%d", max);
return 0;
}
</source>
 
int
Числа вы можете вводить, разделяя их пробелом <code>SPACE</code>, символом табуляции <code>TAB</code>, или нажимая после каждого введенного числа <code>ENTER</code>. Символы <code>SPACE</code>, <code>TAB</code>, <code>ENTER</code> называются пробельными символами (white space). Функция <code>scanf</code> считывает объекты, разделенные любым числом пробельных символов.
main ()
{
int n = 1, cur, min, max;
double sum = 0, sum_sq = 0;
 
int r
Обратите внимание на второй <code>printf</code> — он имеет два аргумента:
= scanf ("%d", &cur);
assert (r == 1);
sum = min = max = cur;
sum_sq = pow (cur, 2);
 
while (1 == (r = scanf ("%d", &cur))) {
<source lang="c">
if (cur < min) {
printf ("Введите %d чисел: ", n);
min = cur;
</source>
} else if (cur > max) {
max = cur;
}
++n;
sum += cur;
sum_sq += pow (cur, 2);
}
assert (r == EOF);
 
printf (("Range: [%d, %d]\n"
Первый аргумент — это строчка <code>"Введите %d чисел: "</code>, которая задает формат того, что будет печататься. В этой строчке встречается выражение <code>%d</code>, которое соответствует формату «десятичная запись целого числа».
"Count: %d\n"
"Mean: %lg\n"
"StdDev: %lg\n"),
min, max, n, sum / n,
sqrt (sum_sq / n - pow (sum / n, 2)));
 
return 0;
На месте этого выражения будет напечатано на экране компьютера значение второго аргумента, то есть значение переменной <code>n</code>.
}
 
В программе <code>max.c</code> мы впервые встречаемся с ''условным оператором'' <code>if</code> и ''оператором цикла'' <code>for</code>.
 
Оператор условного перехода записывается так:
 
<source lang="c">
if(A) B;
</source>
 
Разбор программы вновь начнем с ее завершения — вызова функции <code >printf</code><ref name="fprintf" /> для вывода результатов ее работы. В данном случае, мы выводим значения переменных <var >min</var>, <var >max</var>, <var >n</var> (обновляемых после успешного чтения каждого очередного элемента числовой последовательности.) Среднее значение считаем как отношение ''суммы'' всех элементов (<var >sum</var>) к их ''количеству'' (<var >n</var>).
Он соответствует предложению
 
''Среднеквадратичное отклонение'' вычислено как корень из [[w:Дисперсия |дисперсии]], которая, в свою очередь, вычислена по правилу «среднее квадратов минус квадрат среднего» — <source lang="c" enclose="none" >sum_sq / n - pow (sum / n, 2)</source> — тривиально следующему из ее определения. Значение используемой нами здесь функции <code >pow</code> — первый аргумент, возведенный в степень, заданную вторым аргументом; значение <code >sqrt</code> — квадратный корень единственного аргумента. Эти функции является частью ''стандартной библиотеки'' и ''объявлены'' в ''заголовке'' <code >math.h</code>.<ref name="pow" /><ref name="sqrt" />
: Если выполнено условие <code>A</code>, то сделать <code>B</code>.
 
Чтение последовательности выполнено в два этапа. Вначале, считанный первым элемент используется для инициализации ''предельных значений'' (<var >min</var>, <var >max</var>) и ''сумм'' (<var >sum</var>, <var >sum_sq</var>).
Оператор <code>for</code> устроен следующим образом:
 
Затем, каждый очередной элемент (все так же считываемый функцией <code >scanf</code><ref name="fscanf" />) подвергается следующей обработке:
<source lang="c">
# сравнивается с текущими значениями переменных <var >min</var>, <var >max</var>; переменные обновляются соответственно;
for(A; B; C ) D;
# значение ''суммы'' (<var >sum</var>) увеличивается на величину текущего элемента; значение ''суммы квадратов'' (<var >sum_sq</var>) — на величину квадрата;
</source>
# кроме того, успешное чтение элемента приводит к ''инкременту'' (увеличению на 1) хранящей количество считанных элементов переменной <var >n</var>.
 
Каждый вызов <source lang="c" enclose="none" >scanf ("%d", &cur)</source> считывает целое десятичное число в десятичной записи (<code >%d</code>) в переменную <code >cur</code>. <em >Предшествующие</em> числу ''пробельные символы'' (пробел, табуляция, [[w:Перевод строки |перевод строки]]) при этом игнорируются, а значит могут быть использованы — в любых сочетаниях — для ''разделения'' элементов последовательности.
Элемент <code>D</code> может быть как одной командой, так и произвольным набором команд, заключенных в ''блок''.
 
Команды объединяются в блок с помощью заключения их в фигурные скобки. В нашем случае <code>D</code> это
 
<source lang="c">
scanf ("%d", &a);
if(a > max)
max = a;
</source>
 
Элемент <code>D</code> называется телом цикла — это то, что будет выполнятся несколько раз. Сколько именно? Это зависит от <code>B</code> — тело <code>D</code> будет выполнятся, пока выполнено условие <code>B</code>.
 
Например, строчка
 
<source lang="c">
for(i = 0 ; i < 10 ; i++) { printf ("Hi!"); }
</source>
 
{{Якорь |while}}
означает 10 раз напечатать слово <code>Hi!</code>. Более подробно:
При чтении элементов последовательности начиная со второго мы используем оператор цикла «пока» <code >while</code>, ''синтаксис'' которого напоминает синтаксис [[../Учимся складывать#if |рассмотренного ранее]] ''условного оператора контекста утверждения'' <code >if</code>:<ref name="while" />
<code >while (<var class="angled" >выражение</var>)
<var class="angled" >тело</var></code>
 
Подобно оператору <code >if</code>, если результат вычисления <var >выражения</var> — ложь (другими словами — 0), <var class="angled" >тело</var> не выполняется и ''реализация'' языка Си просто переходит к следующему ''утверждению'' (англ. {{lang |en|statement}}) кода.
# Положим <code>i = 0</code>;
# Проверим, что <code>i < 10</code>; если условие не выполнено, то перейдем к пункту 5;
# Выполним <code>printf ("Hi!")</code>;
# Выполним <code>i++</code> (увеличим переменную <code>i</code> на 1) и перейдем к пункту 2;
# Конец цикла;
 
Если же результат — истина (отличен от 0), выполняется <var class="angled" >тело</var>, после чего реализация <em >вновь возвращается</em> к началу цикла.
Логику оператора <code>for</code> можно изобразить в виде блок-схемы:
 
В нашем случае, ''условием продолжения'' выполнения цикла является равенство единице количества успешно считанных <code >scanf</code> значений. Тем самым, цикл можно прочитать следующим образом:
[[Изображение:for.gif]]
: ''пока'' удается считать очередное значение?
:: обновляй переменные <var >min</var>, <var >max</var>, <var >n</var>, <var >sum</var>, <var >sum_sq</var>.
 
После завершения чтения последовательности, мы ''требуем'' (используя [[../Учимся складывать#assert |уже известную]] нам макроподстановку <code >assert</code><ref name="assert" />) равенства последнего возвращенного функцией <code >scanf</code> значения константе (макроопределению) <code >EOF</code> — что соответствует достижению ''конца файла'' для ''стандартного ввода''. Иными словами, чтение последовательности не должно быть прервано наличием во входном потоке «нечисловых» данных.
Логика нашей программы следующая:
 
== Задания ==
# Объявляем все переменные, которые встретятся в нашей программе:
#* <code>n</code> — количество чисел,
#* <code>i</code> — переменная цикла,
#* <code>max</code> — переменная для хранения текущего найденного максимума,
#* <code>a</code> — переменная с очередным числом, в неё мы считываем каждое число.
# Печатаем на экран приглашение ввести число <code>n</code>.
# Считываем <code>n</code>.
# Печатаем на экран приглашение «Введите <code>n</code> чисел».
# Считываем первое из этих <code>n</code> чисел в переменную <code>max</code>.
# В цикле <code>n - 1</code> раз считываем очередное число, и если оно больше, чем текущий максимум, то обновляем переменную <code>max</code>.
# Выводим значение переменной <code>max</code> .
 
# Измените [[#intstats.c |программу]] для вычисления предельных значений и статистик последовательности [[w:Число с плавающей запятой |чисел с плавающей запятой.]]
Вы уже наверное заметили, что когда мы в <code>scanf</code> указываем переменные, в которые нужно поместить считываемые данные, то перед именем переменной ставим знак <code>&amp;</code> (амперсанд), в то время как в <code>printf</code> при печати значений переменных амперсанд не ставится.
# В [[#Дано |условии задачи]] мы потребовали наличия в последовательности хотя бы одного элемента. Определите, какие сложности возникнут при попытке обобщить данную программу на случай ''пустой'' последовательности?
# Дополните программу выводом «промежуточных» значений — суммы элементов последовательности и суммы их квадратов.
# Реализуйте также вычисление и вывод [[w:Моменты случайной величины |моментов случайной величины]] вплоть до четвертого и, на их основе, — [[w:Коэффициент асимметрии |коэффициента асимметрии]] и [[w:Коэффициент эксцесса |коэффициента эксцесса]].
 
== См. также ==
Операция «амперсанд» — это операция взятия адреса. Если <code>a</code> есть значение переменной <code>a</code>, то <code>&amp;a</code> — это адрес в памяти компьютера, где хранится переменная <code>a</code>: одно есть само число, другое — место, где это число хранится.
* {{w |Математическое ожидание}}
* {{w |Среднеквадратическое отклонение}}
 
== Примечания ==
Функции <code>scanf</code> нужно знать не текущее значение переменной, а её адрес, чтобы положить туда результат считывания. Функции <code>printf</code> нужно знать значения переменных, чтобы их печатать. Функции <code>scanf</code> нужно передавать адреса переменных, куда помещать считываемые данные.
{{Примечания | refs =
<!-- Пожалуйста поддерживайте алфавитный порядок для name. Спасибо. -->
<ref name="assert" >{{Cite web | title = 7.2.1.1 The assert macro | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=204 | work = WG14 N1570 Committee Draft | publisher = ISO/IEC | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="fprintf" >{{Cite web | title = 7.21.6.1 The fprintf function | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=327 | work = WG14 N1570 Committee Draft | publisher = ISO/IEC | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="fscanf" >{{Cite web | title = 7.21.6.2 The fscanf function | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=335 | work = WG14 N1570 Committee Draft | publisher = ISO/IEC | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="pow" >{{Cite web | title = 7.12.7.4 The pow functions | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=266 | work = WG14 N1570 Committee Draft | publisher = ISO/IEC | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="sqrt" >{{Cite web | title = 7.12.7.5 The sqrt functions | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=267 | work = WG14 N1570 Committee Draft | publisher = ISO/IEC | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
<ref name="while" >{{Cite web | title = 6.8.5.1 The while statement | url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#page=169 | work = WG14 N1570 Committee Draft | publisher = ISO/IEC | datepublished = 2011-04-12 | lang = en | accessdate = 2012-11-19}}</ref>
}}
 
{{BookCat}}