Самостоятельная работа 4: циклы и последовательности I

Кувшинов Д.Р.

2015


Общее оглавление


6 баллов

Цели и критерии

Цели: приобретение навыков реализации простейших линейных операций над последовательностями, простейшего тестирования, использования статических массивов.

Критерии полноты

В каждом варианте указано, что требуется получить как результат обработки последовательности. При этом предполагается, что последовательность (в массиве или потоке ввода) состоит из чисел с плавающей точкой и может быть любой длины. Требуется

  1. Написать функцию, вычисляющую требуемый результат, обрабатывая как последовательность произвольный массив (функция должна принимать адрес и размер массива, либо диапазон, заданный парой указателей).
  2. Написать функцию, возвращающую код ошибки или булевское значение (успех или неуспех проверки), выполняющую тестирование функции из п.1 на не менее чем двух массивах, заданных непосредственно в ней (т.е. предполагаемый результат работы можно посчитать “вручную” заранее).
  3. Написать функцию, вычисляющую требуемый результат, читая числа последовательности произвольной длины (и не храня их все в памяти) со стандартного потока ввода до первой ошибки или конца ввода.
  4. Написать программу, выводящую результат теста из п.2 и позволяющую пользователю ввести последовательность и получить результат работы функции из п.3.


Примеры

Данная работа предполагает знание материала по циклам и одномерным массивам.


Отношение количества положительных к количеству отрицательных

Дана последовательность, требуется вычислить отношение числа положительных элементов в последовательности к числу отрицательных элементов. Поведение для пустой последовательности не определено.

Пример: { 4, 1, 100, 0, 20, 0, –1, –2, 3, –6 } → 5/3 = 1.(6).

Решение

При прохождении последовательности достаточно завести две переменные: количество встреченных положительных чисел и количество встреченных отрицательных чисел. Когда последовательность кончится, вернуть их отношение. Обратите внимание на явное преобразование типа в возвращаемом выражении (инструкция return) — целочисленное деление здесь не годится.

// П.1: обработка произвольного массива, переданного как адрес + размер,
// size_t -- стандартный тип (определён в Стандартной библиотеке C),
// предназначенный для хранения размеров массивов.
double pn_ratio(const double numbers[], size_t n)
{
  // Счётчики положительных и отрицательных чисел.
  size_t positives = 0, negatives = 0;
  for (size_t i = 0; i < n; ++i)
  {
    // Ключевое слово auto предписывает компилятору вывести тип переменной
    // автоматически по типу инициализирующего выражения.
    const auto x = numbers[i]; // следующий элемент последовательности
    if (x < 0.0)
      ++negatives;
    else if (x > 0.0)
      ++positives;
  }

  // Без приведения к double будет деление в целых числах.
  // В случае positives != 0 и negatives == 0 возвращает бесконечность.
  return double(positives) / negatives;
}

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

// П.2: тестирование функции из п.1 на заранее заданных массивах.
bool test_pn_ratio()
{
  const double test1[] = { 4, 1, 100, 0, 20, 0, -1, -2, 3, -6 };
  // sizeof возвращает размер статического массива в байтах
  // (!) при этом массив должен быть виден в данном контексте непосредственно,
  // (!) иначе программист рискует получить вместо размера массива размер указателя на него
  if (pn_ratio(test1, sizeof(test1) / sizeof(double)) != 5.0 / 3.0)
    return false;

  const double test2[] = { -40, -2, -111, 42, 0, 0, 2, -1000, -4 };
  if (pn_ratio(test2, sizeof(test2) / sizeof(double)) != 2.0 / 5.0)
    return false;

  // Все проверки прошли успешно.
  return true;
}

Алгоритм, обрабатывающий последовательность, считываемую по одному элементу из потока ввода, по существу не отличается от алгоритма, обрабатывающего массив. Вместо текущего индекса есть текущий (последний считанный) элемент и состояние потока.

Тип istream — стандартный тип, к которому также принадлежит и объект стандартного потока ввода cin. Использование ссылки in позволяет вызвать функцию для любого потока ввода.

Последнее пригодится для того чтобы написать для этой функции тест, аналогичный тесту с массивами, в котором будет использоваться пользовательский объект потока с заранее помещёнными в него числами. Подробнее см. ниже.

// П.3: считывание чисел с потока ввода.
double pn_ratio(istream &in)
{
  // Счётчики положительных и отрицательных чисел.
  size_t positives = 0, negatives = 0;
  for (double x; in >> x;)
  {
    if (x < 0.0)
      ++negatives;
    else if (x > 0.0)
      ++positives;
  }

  // Без приведения к double будет деление в целых числах.
  return double(positives) / negatives;
}

C++, HTML


Евклидова норма

Вычислить евклидову норму последовательности как многомерного вектора. Евклидова норма вектора определяется как корень квадратный из суммы квадратов компонент вектора.

Пример: Пример: { 1, –5, 2, 20, –13 } → sqrt(1 + 25 + 4 + 400 + 169) ≈ 24.4744765.

Решение

Алгоритм достаточно прост: накопить сумму квадратов в отдельной переменной s (название может быть любое) и вернуть квадратный корень от s.

// П.1: обработка произвольного массива, переданного как адрес + размер,
// size_t -- стандартный тип (определён в Стандартной библиотеке C),
// предназначенный для хранения размеров массивов.
double euclid_norm(const double a[], size_t n)
{
  double s = 0.0;
  for (size_t i = 0; i < n; ++i)
    s += a[i] * a[i];
  return sqrt(s);
}

// П.3: считывание чисел с потока ввода.
double euclid_norm(istream &in)
{
  double s = 0.0;
  for (double x; in >> x;)
    s += x * x;
  return sqrt(s);
}

Тестирование сравнивает результат функции с приближённо вычисленным ожидаемым результатом, поэтому используются вспомогательные конструкции: точность сравнения (допустимая погрешность) — константа TOLERANCE и функция сравнения на равенство с заданной точностью almost_equal.

// Чувствительность к погрешности при проверке нормы.
const double TOLERANCE = 1e-6;

// Сравнение двух значений на примерное равенство
// с заданным уровнем относительной разности tolerance.
// В данном примере используется при тестировании.
bool almost_equal(double x1, double x2, double tolerance = TOLERANCE)
{
  return abs(x1 - x2) <= tolerance * fmax(abs(x1), abs(x2));
}

// П.2: тестирование функции из п.2 на заранее заданных массивах.
bool test_euclid_norm()
{
  const double test1[] = { 4, 1, 0, 3, 0, -1, -2, -6 };
  // sizeof возвращает размер статического массива в байтах
  // (!) при этом массив должен быть виден в данном контексте непосредственно,
  // (!) иначе программист рискует получить вместо размера массива размер указателя на него
  if (!almost_equal(
        euclid_norm(test1, sizeof(test1) / sizeof(double)),
        8.18535277))
    return false;

  const double test2[] = { -40, -2, -111, 42, 2, -1000, -4 };
  if (!almost_equal(
        euclid_norm(test2, sizeof(test2) / sizeof(double)),
        1007.823893))
    return false;

  // Все тесты прошли успешно.
  return true;
}

C++, HTML


См. также дополнительный пример на основе примера с евклидовой нормой — использование строкового потока ввода-вывода для автоматического тестирования строкого варианта функции euclid_norm. См. также iostreams, особенно часть, посвящённую строковым потокам.


Варианты

1

Размах последовательности (разность между максимумом и минимумом). Размах пустой последовательности считается равным нулю.

Пример: { 1, 10, –12, 4, 12, 14, 0 } → размах 26 (максимум = 14, минимум = –12).

2

Разность между суммой всех чётных и суммой всех нечётных чисел в последовательности.

Пример: { 10, 4, 5, 0, –15, 2, 7, –5, 0 } → 24 = (10 + 4 + 0 + 2 + 0) – (5 – 15 + 7 – 5).

3

Арифметическое среднее элементов последовательности (сумму всех элементов поделить на их количество)

Пример: { 1, 5, 10, –4, 8, 8, 0 } → (1 + 5 + 10 – 4 + 8 + 8 + 0)/7 = 4.

4

Геометрическое среднее элементов последовательности (корень степени, равной количеству элементов от произведения всех элементов).

Пример: { 9, 9, 10, 24, 25, 65 } → (9*9*10*24*25*65)1/6 ≈ 17.779721.

5

Определить длину последнего участка последовательности (не короче двух элементов), содержащего строго возрастающую подпоследовательность.

Пример: { 1, 2, 3, 4, 4, 1, 0, –1, 2, 3, –2, –2 } → 3.

6

Разность между частным суммы модулей элементов последовательности и максимума из модулей элементов последовательности и единицей.

Пример: { 4, 10, –12, 4, 12, 14, 0 } → (4 + 10 + 12 + 4 + 12 + 14 + 0) / 14 – 1 = 3.

7

Геометрическое среднее элементов последовательности (корень степени, равной количеству элементов от произведения всех элементов) через арифметическое среднее логарифмов как e в степени арифметическое среднее логарифмов элементов.

Пример: { 9, 9, 10, 24, 25, 65 } → (9*9*10*24*25*65)1/6 = exp((ln 9 + ln 9 + ln 10 + ln 24 + ln 25 + ln 65)/6) ≈ exp(2.8780585) ≈ 17.779721.

8

Определить, сколько раз в последовательности встречаются пары соседних элементов, равных нулю.

Пример: { 1, 0, 0, 0, 10, 0, 12, 0, 0 } → 3.

9

Определить длину последнего участка последовательности, состоящего из идущих подряд нулей.

Пример: { 0, 0, 0, 1, 2, 3, 4, 0, 0, 5, 6, 7 } → 2.

10

Найти максимальную по модулю разность соседних элементов последовательности. Для пустой последовательности возвращать 0.

Пример: { 1, 10, 8, 7, 1, 0, 10, 4 } → max( 9, 2, 1, 6, 1, 10, 6 ) = 10.

11

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

Пример: { 4, 1, 0, 5, 4, 2, 1 } → минимум = 0, позиция 2; максимум = 5, позиция 3 → расстояние между ними = 1.

12

В последовательности найти разность между наименьшим положительным числом и наибольшим отрицательным числом.

Пример: { 8, –10, –4, –14, 5, 15, 2, –6, 3, –3 } → (2) – (–3) → 5.

13

Определить, сколько раз в последовательности целых чисел встречаются пары соседних элементов, чётность которых совпадает (т.е. чётное-чётное или нечётное-нечётное).

Пример: { 1, –4, 2, 3, 4, 4, 3, 1, 1, 5, –5 } → –4 и 2, 4 и 4, 3 и 1, 1 и 1, 1 и 5, 5 и –5 → 6.

14

Дана последовательность чисел в плавающей точке. Определить, сколько из них являются целыми числами.

Пример: { 0.0, –1.5, 18.0, 22.001, –1.0 } → 0.0, 18.0 и –1.0 — целые → 3.

15

В последовательности найти сумму наибольшего положительного числа и наименьшего отрицательного числа.

Пример: { 8, –10, –4, –14, 5, 15, 2, –6, 3, –3 } → 15 + (–14) → 1.


Общее оглавление

Кувшинов Д.Р. © 2015