Цели: приобретение навыков реализации простейших линейных операций над последовательностями, простейшего тестирования, использования статических массивов.
Критерии полноты
В каждом варианте указано, что требуется получить как результат обработки последовательности. При этом предполагается, что последовательность (в массиве или потоке ввода) состоит из чисел с плавающей точкой и может быть любой длины. Требуется
Данная работа предполагает знание материала по циклам и одномерным массивам.
Дана последовательность, требуется вычислить отношение числа положительных элементов в последовательности к числу отрицательных элементов. Поведение для пустой последовательности не определено.
Пример: { 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;
}
Вычислить евклидову норму последовательности как многомерного вектора. Евклидова норма вектора определяется как корень квадратный из суммы квадратов компонент вектора.
Пример: Пример: { 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;
}
См. также дополнительный пример на основе примера с евклидовой нормой — использование строкового потока ввода-вывода для автоматического тестирования строкого варианта функции euclid_norm. См. также iostreams, особенно часть, посвящённую строковым потокам.
Размах последовательности (разность между максимумом и минимумом). Размах пустой последовательности считается равным нулю.
Пример: { 1, 10, –12, 4, 12, 14, 0 } → размах 26 (максимум = 14, минимум = –12).
Разность между суммой всех чётных и суммой всех нечётных чисел в последовательности.
Пример: { 10, 4, 5, 0, –15, 2, 7, –5, 0 } → 24 = (10 + 4 + 0 + 2 + 0) – (5 – 15 + 7 – 5).
Арифметическое среднее элементов последовательности (сумму всех элементов поделить на их количество)
Пример: { 1, 5, 10, –4, 8, 8, 0 } → (1 + 5 + 10 – 4 + 8 + 8 + 0)/7 = 4.
Геометрическое среднее элементов последовательности (корень степени, равной количеству элементов от произведения всех элементов).
Пример: { 9, 9, 10, 24, 25, 65 } → (9*9*10*24*25*65)1/6 ≈ 17.779721.
Определить длину последнего участка последовательности (не короче двух элементов), содержащего строго возрастающую подпоследовательность.
Пример: { 1, 2, 3, 4, 4, 1, 0, –1, 2, 3, –2, –2 } → 3.
Разность между частным суммы модулей элементов последовательности и максимума из модулей элементов последовательности и единицей.
Пример: { 4, 10, –12, 4, 12, 14, 0 } → (4 + 10 + 12 + 4 + 12 + 14 + 0) / 14 – 1 = 3.
Геометрическое среднее элементов последовательности (корень степени, равной количеству элементов от произведения всех элементов) через арифметическое среднее логарифмов как 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.
Определить, сколько раз в последовательности встречаются пары соседних элементов, равных нулю.
Пример: { 1, 0, 0, 0, 10, 0, 12, 0, 0 } → 3.
Определить длину последнего участка последовательности, состоящего из идущих подряд нулей.
Пример: { 0, 0, 0, 1, 2, 3, 4, 0, 0, 5, 6, 7 } → 2.
Найти максимальную по модулю разность соседних элементов последовательности. Для пустой последовательности возвращать 0.
Пример: { 1, 10, 8, 7, 1, 0, 10, 4 } → max( 9, 2, 1, 6, 1, 10, 6 ) = 10.
Предполагая, что последовательность либо содержит менее двух элементов, либо содержит единственные минимум и максимум, найти расстояние (в позициях) между минимумом и максимумом.
Пример: { 4, 1, 0, 5, 4, 2, 1 } → минимум = 0, позиция 2; максимум = 5, позиция 3 → расстояние между ними = 1.
В последовательности найти разность между наименьшим положительным числом и наибольшим отрицательным числом.
Пример: { 8, –10, –4, –14, 5, 15, 2, –6, 3, –3 } → (2) – (–3) → 5.
Определить, сколько раз в последовательности целых чисел встречаются пары соседних элементов, чётность которых совпадает (т.е. чётное-чётное или нечётное-нечётное).
Пример: { 1, –4, 2, 3, 4, 4, 3, 1, 1, 5, –5 } → –4 и 2, 4 и 4, 3 и 1, 1 и 1, 1 и 5, 5 и –5 → 6.
Дана последовательность чисел в плавающей точке. Определить, сколько из них являются целыми числами.
Пример: { 0.0, –1.5, 18.0, 22.001, –1.0 } → 0.0, 18.0 и –1.0 — целые → 3.
В последовательности найти сумму наибольшего положительного числа и наименьшего отрицательного числа.
Пример: { 8, –10, –4, –14, 5, 15, 2, –6, 3, –3 } → 15 + (–14) → 1.
Кувшинов Д.Р. © 2015