Перейти к содержанию

Язык Си в примерах/Максимум

Материал из Викиучебника — открытых книг для открытого мира

Язык Си в примерах


  1. Компиляция программ
  2. Простейшая программа «Hello World»
  3. Учимся складывать
  4. Максимум
  5. Таблица умножения
  6. ASCII-коды символов
  7. Верхний регистр
  8. Скобочки
  9. Факториал
  10. Степень числа
  11. Треугольник Паскаля
  12. Корень уравнения
  13. Система счисления
  14. Сортировка
  15. Библиотека complex
  16. Сортировка на основе qsort
  17. RPN-калькулятор
  18. RPN-калькулятор на Bison
  19. Простая грамматика
  20. Задача «Расчёт сопротивления схемы»
  21. Простая реализация конечного автомата
  22. Использование аргументов командной строки
  23. Чтение и печать без использования stdio
  24. Декодирование звукозаписи в формате ADX
  25. Другие примеры
  26. XCC C
Дано
непустая последовательность разделенных пробельными символами целых чисел (в «текстовом» десятичном представлении) на стандартном вводе программы. Последовательность конечна, но ее длина заранее неизвестна.
Найти
предельные значения последовательности (минимум, максимум) и основные статистики (среднее, среднеквадратичное отклонение.)
Указания
воспользуйтесь циклом «пока» (while) для чтения последовательности и функциями pow, sqrt для вычисления среднеквадратичного отклонения.

Решение

[править]

#include <assert.h>
#include <math.h>
#include <stdio.h>

int
main ()
{
  int n = 1, cur, min, max;
  double sum, sum_sq;

  int r
    = scanf ("%d", &cur);
  assert (r == 1);
  sum = min = max = cur;
  sum_sq = pow (cur, 2);

  while (1 == (r = scanf ("%d", &cur))) {
    if (cur < min) {
      min = cur;
    } else if (cur > max) {
      max = cur;
    }
    ++n;
    sum     += cur;
    sum_sq  += pow (cur, 2);
  }
  assert (r == EOF);
  assert (! ferror (stdin));

  printf (("Range:  [%d, %d]\n"
           "Count:  %d\n"
           "Mean:   %lg\n"
           "StdDev: %lg\n"),
          min, max, n, sum / n,
          sqrt (sum_sq / n - pow (sum / n, 2)));

  return 0;
}


Вычисление и вывод результатов

[править]

Разбор программы вновь начнем с ее завершения — вызова функции printf[1] для вывода результатов ее работы. В данном случае, мы выводим значения переменных min, max, n (обновляемых после успешного чтения каждого очередного элемента числовой последовательности.) Среднее значение считаем как отношение суммы всех элементов (sum) к их количеству (n).

Среднеквадратичное отклонение вычислено как корень из дисперсии, которая, в свою очередь, вычислена по правилу «среднее квадратов минус квадрат среднего» — sum_sq / n - pow (sum / n, 2) — тривиально следующему из ее определения. Значение используемой нами здесь функции pow — первый аргумент, возведенный в степень, заданную вторым аргументом; значение sqrt — квадратный корень единственного аргумента. Эти функции является частью стандартной библиотеки и объявлены в заголовке math.h.[2][3]

Чтение и накопление

[править]

Чтение последовательности выполнено в два этапа. Вначале, считанный первым элемент используется для инициализации предельных значений (min, max) и сумм (sum, sum_sq).

Затем, каждый очередной элемент (все так же считываемый функцией scanf[4]) подвергается следующей обработке:

  1. сравнивается с текущими значениями переменных min, max; если элемент лежит вне этих пределов — один из них изменяется соответственно;
  2. значение суммы (sum) увеличивается на величину текущего элемента; значение суммы квадратов (sum_sq) — на величину квадрата;
  3. кроме того, успешное чтение элемента приводит к инкременту (увеличению на 1) хранящей количество считанных элементов переменной n.

Каждый вызов scanf ("%d", &cur) считывает целое число в десятичной записи (%d) в переменную cur. Предшествующие числу пробельные символы (пробел, табуляция, перевод строки) при этом игнорируются, а значит могут быть использованы — в любых сочетаниях — для разделения элементов последовательности.

Условия корректности ввода

[править]

После завершения чтения последовательности, мы требуем (используя уже известную нам макроподстановку assert[5]) истинности следующих двух условий, смысл которых сводится к тому, что чтение последовательности не было прервано ни появлением во входном потоке каких-либо «нечисловых» данных, ни ошибкой ввода-вывода.

  1. Мы проверяем равенство последнего возвращенного функцией scanf значения константе (макроопределению) EOF (конец файла; англ. end of file), что соответствует или исчерпанию нашего входного потока (стандартного ввода), или возникновению ошибки ввода-вывода. Другое возможное на данном этапе значение — ноль — укажет на наличие во входном потоке данных, которые не были опознаны функцией scanf как требуемое условием целое число в десятичной записи (%d).
  2. Если предыдущее требование выполняется (иными словами — останов произошел не из-за «нечислового» ввода), мы также явно требуем ложности значения функции признака ошибки ferror для стандартного ввода (stdin.)[6]

Цикл «пока»

[править]

При чтении элементов последовательности начиная со второго мы используем оператор цикла «пока» while, синтаксис которого напоминает синтаксис рассмотренного ранее условного оператора контекста утверждения if:[7]

while (выражение)
  тело

Подобно оператору if, если результат вычисления выражения — ложь (другими словами — 0), тело не выполняется и реализация языка Си просто переходит к следующему утверждению (англ. statement) кода.

Если же результат — истина (отличен от 0), выполняется тело, после чего реализация вновь возвращается к началу цикла.

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

пока удается считать очередное значение?
обновляй переменные min, max, n, sum, sum_sq.

Задания

[править]
  1. Измените программу для вычисления предельных значений и статистик последовательности чисел с плавающей запятой.
  2. В условии задачи мы потребовали наличия в последовательности хотя бы одного элемента. Определите, какие сложности возникнут при попытке обобщить данную программу на случай пустой последовательности?
  3. Дополните программу выводом «промежуточных» значений — суммы элементов последовательности и суммы их квадратов.
  4. Реализуйте также вычисление и вывод моментов случайной величины вплоть до четвертого и, на их основе, — коэффициента асимметрии и коэффициента эксцесса.

См. также

[править]

Примечания

[править]
  1. 7.21.6.1 The fprintf function(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  2. 7.12.7.4 The pow functions(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  3. 7.12.7.5 The sqrt functions(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  4. 7.21.6.2 The fscanf function(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  5. 7.2.1.1 The assert macro(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  6. 7.21.10.3 The ferror function(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  7. 6.8.5.1 The while statement(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.