← До переліку лабораторних робіт

Лабораторна робота №2. Розгалужені обчислювальні процеси

Варіант 19 · Виконав: Одарчук Олексій, КНУ імені Тараса Шевченка, ФІТ, група ІПЗ-11 · C++17, g++

1. Мета роботи

2. Умова задачі

Завдання 1

Визначити значення функції залежно від значення її аргументу, який вводиться з клавіатури (таблиця 2.1, варіант 19):

         | 4 − x²,   якщо 0 < x < 4
         | 0,        якщо x = 0
f(x)  =  | x³,       якщо x < 0
         | 1,        якщо x > 4

Завдання 2

Увести з клавіатури дійсні числа x, y. Визначити, чи належить точка з координатами (x, y) зафарбованій частині площини (таблиця 2.2, варіант 19).

Рисунок до варіанта 19 (таблиця 2.2)
Рисунок до варіанта 19 (таблиця 2.2)

3. Аналіз задачі та теоретичне обґрунтування

Завдання 1

Умови варіанта є діапазонними (нерівності над дійсним числом), тому оператор switch тут незастосовний: за синтаксисом мови вираз-селектор не може належати до дійсного типу. Використано ланцюжок if … else if … else. Гілки впорядковано так, щоб кожна наступна перевірка виконувалась лише для аргументів, які не потрапили в попередні: спочатку x < 0, потім x == 0, далі x < 4, і нарешті x > 4. Це усуває потребу в складених умовах виду 0 < x && x < 4 і зменшує кількість порівнянь.

Окремо слід зазначити прогалину в умові варіанта: інтервали задано строгими нерівностями 0 < x < 4 та x > 4, тому точка x = 4 не покривається жодною з чотирьох гілок. Довільно доозначувати функцію в цій точці некоректно, тому програма повідомляє, що функцію не визначено, — це відповідає умові буквально.

Завдання 2

На рисунку варіанта подано дві зафарбовані фігури.

Фігура 1 — кільце. Зафарбовано частину площини між двома концентричними колами з центром у початку координат: внутрішній радіус 0,5, зовнішній 1. Оскільки на рисунку межі суцільні, вони належать зафарбованій частині:

0,5 ≤ √(x² + y²) ≤ 1

Фігура 2 — ромб із вершинами (−0,5; 0), (0; 1), (0,5; 0), (0; −1). Ромб із півдіагоналями a = 0,5 по осі Ox та b = 1 по осі Oy описується канонічною нерівністю:

|x| / 0,5 + |y| / 1 ≤ 1,   тобто  2·|x| + |y| ≤ 1

Рисунок варіанта подає обидві фігури в окремих системах координат і не вказує однозначно, чи мається на увазі одна з них, чи їх об'єднання. Тому програма не робить довільного вибору за умову, а обчислює належність окремо до кожної фігури та до їх об'єднання; потрібний режим користувач обирає в меню. Меню реалізовано оператором switch — це поліваріантний вибір за цілим селектором, для якого switch є природною конструкцією.

Порівняння дійсних чисел виконується з допуском EPS = 1e-9: точка, що лежить точно на межі фігури, має зараховуватись як така, що належить фігурі, а пряме порівняння <= для результату sqrt() цього не гарантує через похибку округлення.

Рекомендована література (зі списку методичних вказівок): Ковалюк Т.В. Алгоритмізація та програмування. – Львів.: «Магнолія 2006», 2024. – 400 с.; Deitel P., Deitel H. C++ How to Program. Pearson Education, Inc. Hoboken, New Jersey. 2017. – 3015 p.

4. Блок-схема алгоритму

Рисунок 1 — Блок-схема головної функції main() завдання 1
Рисунок 1 — Блок-схема головної функції main() завдання 1
Рисунок 2 — Блок-схема головної функції main() завдання 2
Рисунок 2 — Блок-схема головної функції main() завдання 2

Схеми побудовано з текстів програм за допомогою rombik (rombik.app) відповідно до ДСТУ 19.701-90 (ISO 5807).

5. Текст програми

Завдання 1 — task1.cpp

Завантажити файл task1.cpp

/*==============================================================================
  Лабораторна робота №2. Завдання 1. Варіант 19.
  Тема: розгалужені обчислювальні процеси.

  Умова: визначити значення функції залежно від значення її аргументу,
         що вводиться з клавіатури (таблиця 2.1, варіант 19):

                  | 4 - x^2,  0 < x < 4
                  | 0,        x = 0
           f(x) = | x^3,      x < 0
                  | 1,        x > 4

  Зауваження щодо області визначення: умови таблиці не покривають точку
  x = 4 (інтервал заданий строгими нерівностями 0 < x < 4 та x > 4).
  Тому x = 4 обробляється як точка, в якій функцію не визначено, — про це
  виводиться окреме повідомлення, а не довільно домальоване значення.

  Виконав: Одарчук Олексій, КНУ імені Тараса Шевченка, ФІТ, група ІПЗ-11.

  Компілятор: g++ -std=c++17
==============================================================================*/

#include <iostream>

/* Код результату обчислення функції: чи визначено її в заданій точці. */
enum FuncStatus { FUNC_OK, FUNC_UNDEFINED };

/*------------------------------------------------------------------------------
  evaluate — обчислити значення кусково-заданої функції f(x).

  Параметри:
      x      [вхідний]  — аргумент функції;
      result [вихідний] — адреса змінної для значення функції;
                          заповнюється лише при поверненні FUNC_OK.

  Повертає: FUNC_OK        — функцію визначено, значення записано в *result;
            FUNC_UNDEFINED — функцію не визначено в точці x (випадок x = 4).

  Гілки реалізовано вкладеною конструкцією if ... else if ... else, оскільки
  умови є діапазонними: оператор switch тут незастосовний, бо його
  вираз-селектор не може належати до дійсного типу.
------------------------------------------------------------------------------*/
FuncStatus evaluate(double x, double *result)
{
    if (x < 0.0) {
        *result = x * x * x;
    } else if (x == 0.0) {
        *result = 0.0;
    } else if (x < 4.0) {
        *result = 4.0 - x * x;
    } else if (x > 4.0) {
        *result = 1.0;
    } else {
        return FUNC_UNDEFINED; /* залишається єдиний випадок x = 4 */
    }

    return FUNC_OK;
}

/*------------------------------------------------------------------------------
  branchName — текстова назва гілки, яку задіяно для заданого аргументу.
  Використовується для протоколювання: показує, яка саме умова спрацювала.

  Параметри: x [вхідний] — аргумент функції.
  Повертає : рядок-константу з описом гілки.
------------------------------------------------------------------------------*/
const char *branchName(double x)
{
    if (x < 0.0)
        return "x < 0      ->  f(x) = x^3";
    if (x == 0.0)
        return "x = 0      ->  f(x) = 0";
    if (x < 4.0)
        return "0 < x < 4  ->  f(x) = 4 - x^2";
    if (x > 4.0)
        return "x > 4      ->  f(x) = 1";
    return "x = 4      ->  функцію не визначено";
}

/*------------------------------------------------------------------------------
  readArgument — прочитати дійсне число з контролем коректності введення.

  Параметри: x [вихідний] — адреса змінної для аргументу функції.
  Повертає : true — число прочитано; false — вхідні дані вичерпано.
------------------------------------------------------------------------------*/
bool readArgument(double *x)
{
    std::cout << "Уведіть аргумент x = ";

    while (!(std::cin >> *x)) {
        if (std::cin.eof())
            return false;

        std::cin.clear();            /* зняти прапорець помилки */
        std::cin.ignore(1024, '\n'); /* очистити буфер клавіатури */
        std::cout << "Помилка: очікується дійсне число. Повторіть: ";
    }
    return true;
}

/*------------------------------------------------------------------------------
  Головна функція. Організовує введення аргументу, виклик обчислення
  та виведення результату.

  Локальні змінні:
      x      — аргумент функції, введений з клавіатури;
      y      — обчислене значення функції;
      status — ознака визначеності функції в точці x.
------------------------------------------------------------------------------*/
int main()
{
    std::cout << "Лабораторна робота №2, завдання 1 (варіант 19)\n"
                 "Виконав: студент групи ІПЗ-11 Одарчук Олексій\n"
                 "Обчислення кусково-заданої функції f(x):\n"
                 "    4 - x^2,  якщо 0 < x < 4\n"
                 "    0,        якщо x = 0\n"
                 "    x^3,      якщо x < 0\n"
                 "    1,        якщо x > 4\n"
              << std::endl;

    double x = 0.0;
    if (!readArgument(&x)) {
        std::cout << "\nВхідні дані вичерпано." << std::endl;
        return 1;
    }

    double y = 0.0;
    const FuncStatus status = evaluate(x, &y);

    /* Дійсні числа виводяться у фіксованому форматі з шістьма знаками. */
    std::cout.setf(std::ios::fixed);
    std::cout.precision(6);

    std::cout << "\nАргумент:  x = " << x << std::endl;
    std::cout << "Гілка:     " << branchName(x) << std::endl;

    if (status == FUNC_UNDEFINED) {
        std::cout << "Результат: функцію не визначено в точці x = 4 "
                     "(умова варіанта не покриває цю точку)."
                  << std::endl;
        return 2;
    }

    std::cout << "Результат: f(" << x << ") = " << y << std::endl;
    return 0;
}

Завдання 2 — task2.cpp

Завантажити файл task2.cpp

/*==============================================================================
  Лабораторна робота №2. Завдання 2. Варіант 19.
  Тема: розгалужені обчислювальні процеси.

  Умова: увести з клавіатури дійсні числа x, y. Визначити, чи належить точка
         з координатами (x, y) зафарбованій частині площини
         (таблиця 2.2, варіант 19).

  Опис фігур із рисунка варіанта.
      Фігура 1 — кільце: зафарбовано частину площини між двома концентричними
      колами з центром у початку координат, внутрішній радіус 0,5, зовнішній 1.
      Межі кіл на рисунку суцільні, тобто належать зафарбованій частині:
                        0,5 <= sqrt(x^2 + y^2) <= 1.

      Фігура 2 — ромб із вершинами (-0,5; 0), (0; 1), (0,5; 0), (0; -1).
      Канонічне рівняння ромба з півдіагоналями a = 0,5 (по осі Ox)
      та b = 1 (по осі Oy):
                        |x| / 0,5 + |y| / 1 <= 1,   тобто  2*|x| + |y| <= 1.

  Оскільки на рисунку варіанта подано дві фігури в окремих системах координат,
  програма визначає належність точки кожній фігурі окремо та їх об'єднанню.
  Режим перевірки обирає користувач у меню (оператор switch).

  Виконав: Одарчук Олексій, КНУ імені Тараса Шевченка, ФІТ, група ІПЗ-11.

  Компілятор: g++ -std=c++17
==============================================================================*/

#include <iostream>
#include <cmath>

/* Півдіагоналі ромба та радіуси кільця — константи умови варіанта. */
const double R_INNER = 0.5; /* внутрішній радіус кільця */
const double R_OUTER = 1.0; /* зовнішній радіус кільця  */
const double RHOMB_A = 0.5; /* півдіагональ ромба по осі Ox */
const double RHOMB_B = 1.0; /* півдіагональ ромба по осі Oy */

/* Допуск для порівняння дійсних чисел: точка на самій межі фігури має
   зараховуватись як така, що належить фігурі, попри похибку округлення. */
const double EPS = 1e-9;

/*------------------------------------------------------------------------------
  inRing — чи належить точка кільцю 0,5 <= r <= 1.

  Параметри: x, y [вхідні] — координати точки.
  Повертає : true — точка належить кільцю (разом з межами).

  Локальні змінні:
      r — відстань від початку координат до точки.
------------------------------------------------------------------------------*/
bool inRing(double x, double y)
{
    const double r = std::sqrt(x * x + y * y);
    return r >= R_INNER - EPS && r <= R_OUTER + EPS;
}

/*------------------------------------------------------------------------------
  inRhombus — чи належить точка ромбу |x|/a + |y|/b <= 1.

  Параметри: x, y [вхідні] — координати точки.
  Повертає : true — точка належить ромбу (разом з межами).
------------------------------------------------------------------------------*/
bool inRhombus(double x, double y)
{
    return std::fabs(x) / RHOMB_A + std::fabs(y) / RHOMB_B <= 1.0 + EPS;
}

/*------------------------------------------------------------------------------
  readChoice — прочитати номер пункту меню (1..3) з контролем введення.

  Параметри: choice [вихідний] — адреса змінної для номера пункту.
  Повертає : true — номер прочитано; false — вхідні дані вичерпано.
------------------------------------------------------------------------------*/
bool readChoice(int *choice)
{
    std::cout << "Оберіть фігуру (1..3): ";

    while (!(std::cin >> *choice) || *choice < 1 || *choice > 3) {
        if (std::cin.eof())
            return false;

        std::cin.clear();            /* зняти прапорець помилки */
        std::cin.ignore(1024, '\n'); /* очистити буфер клавіатури */
        std::cout << "Помилка: потрібне число 1, 2 або 3. Повторіть: ";
    }
    return true;
}

/*------------------------------------------------------------------------------
  readPoint — прочитати координати точки з контролем коректності введення.

  Параметри: x, y [вихідні] — адреси змінних для координат точки.
  Повертає : true — координати прочитано; false — вхідні дані вичерпано.
------------------------------------------------------------------------------*/
bool readPoint(double *x, double *y)
{
    std::cout << "Уведіть координати точки x та y: ";

    while (!(std::cin >> *x >> *y)) {
        if (std::cin.eof())
            return false;

        std::cin.clear();
        std::cin.ignore(1024, '\n');
        std::cout << "Помилка: очікуються два дійсних числа. Повторіть: ";
    }
    return true;
}

/*------------------------------------------------------------------------------
  yesNo — слово «так» або «ні» для логічного значення.

  Параметри: value [вхідний] — логічне значення.
  Повертає : рядок-константу «так» або «ні».
------------------------------------------------------------------------------*/
const char *yesNo(bool value)
{
    if (value)
        return "так";
    return "ні";
}

/*------------------------------------------------------------------------------
  Головна функція. Виводить меню вибору фігури, читає координати точки
  та повідомляє результат перевірки.

  Локальні змінні:
      choice  — номер пункту меню, обраний користувачем;
      x, y    — координати точки;
      ring    — ознака належності точки кільцю;
      rhomb   — ознака належності точки ромбу;
      belongs — ознака належності точки обраній фігурі;
      figure  — назва обраної фігури для повідомлення.
------------------------------------------------------------------------------*/
int main()
{
    std::cout << "Лабораторна робота №2, завдання 2 (варіант 19)\n"
                 "Виконав: студент групи ІПЗ-11 Одарчук Олексій\n"
                 "Належність точки зафарбованій частині площини\n\n"
                 "  1 - кільце 0,5 <= sqrt(x^2 + y^2) <= 1\n"
                 "  2 - ромб |x|/0,5 + |y|/1 <= 1\n"
                 "  3 - об'єднання обох фігур\n"
              << std::endl;

    int choice = 0;
    if (!readChoice(&choice)) {
        std::cout << "\nВхідні дані вичерпано." << std::endl;
        return 1;
    }

    double x = 0.0, y = 0.0;
    if (!readPoint(&x, &y)) {
        std::cout << "\nВхідні дані вичерпано." << std::endl;
        return 1;
    }

    const bool ring = inRing(x, y);
    const bool rhomb = inRhombus(x, y);

    /* Дійсні числа виводяться у фіксованому форматі з чотирма знаками. */
    std::cout.setf(std::ios::fixed);
    std::cout.precision(4);

    std::cout << "\nТочка: x = " << x << ", y = " << y << std::endl;
    std::cout << "  відстань від початку координат r = " << std::sqrt(x * x + y * y)
              << std::endl;
    std::cout << "  значення |x|/0,5 + |y|/1        = "
              << std::fabs(x) / RHOMB_A + std::fabs(y) / RHOMB_B << std::endl;

    bool belongs = false;
    const char *figure;

    /* Поліваріантний вибір: кожна гілка завершується оператором break,
       інакше керування «провалилося» б у наступну гілку. */
    switch (choice) {
    case 1:
        belongs = ring;
        figure = "кільцю";
        break;
    case 2:
        belongs = rhomb;
        figure = "ромбу";
        break;
    case 3:
        belongs = ring || rhomb;
        figure = "об'єднанню кільця та ромба";
        break;
    default:
        std::cout << "Неіснуючий пункт меню." << std::endl;
        return 2;
    }

    if (belongs)
        std::cout << "\nНалежність " << figure << ": ТАК — точка у зафарбованій частині"
                  << std::endl;
    else
        std::cout << "\nНалежність " << figure
                  << ": НІ — точка поза зафарбованою частиною" << std::endl;

    std::cout << "\nДовідково: кільце - " << yesNo(ring) << ", ромб - " << yesNo(rhomb)
              << std::endl;
    return 0;
}

6. Результати виконання роботи

Компіляція: make (g++ -std=c++17, прапорці -Wall -Wextra -pedantic -O2). Попереджень компілятора немає.

Нижче наведено екранні копії повних прогонів програм: кожна починається з запуску програми, містить усе введення з клавіатури й увесь вивід до завершення роботи. Прогін, що не вміщується на один екран, подано кількома послідовними частинами. Протоколи всіх прогонів, зокрема додаткових наборів вхідних даних, винесено окремими файлами за посиланнями.

Екранна копія 1 — завдання 1: п'ять запусків — усі гілки функції (x = −2, 0, 2, 7) та точка x = 4, у якій функцію не визначено
Екранна копія 1 — завдання 1: п'ять запусків — усі гілки функції (x = −2, 0, 2, 7) та точка x = 4, у якій функцію не визначено

Повний протокол виконання (task1.txt) — повний протокол, 104 рядків

Екранна копія 2 — завдання 2: три запуски — належність точок кільцю, ромбу та їх об'єднанню
Екранна копія 2 — завдання 2: три запуски — належність точок кільцю, ромбу та їх об'єднанню

Повний протокол виконання (task2.txt) — повний протокол, 159 рядків

7. Аналіз достовірності результатів

Достовірність результатів перевірено аналітично: значення функції обчислено вручну для кожної гілки, а належність точок фігурам — підстановкою координат у нерівності кільця 0,5 ≤ √(x²+y²) ≤ 1 та ромба |x|/0,5 + |y|/1 ≤ 1. Для кожної контрольної величини поруч із розрахунком наведено результат програми.

Завдання 1 — перевірка кожної гілки

x Задіяна гілка Аналітичний розрахунок Результат програми
−2 x < 0 (−2)³ = −8 −8,000000
−0,5 x < 0 (−0,5)³ = −0,125 −0,125000
0 x = 0 0 0,000000
2 0 < x < 4 4 − 2² = 0 0,000000
3,9 0 < x < 4 4 − 15,21 = −11,21 −11,210000
4 не визначено умовою повідомлення про невизначеність
7 x > 4 1 1,000000

Задіяно всі чотири гілки функції та випадок невизначеності; усі обчислені значення збігаються з ручним розрахунком.

Завдання 2 — перевірка на характерних точках

Точка (x; y) r = √(x²+y²) |x|/0,5 + |y| Кільце (аналітично) Ромб (аналітично) Результат програми
(0,8; 0) 0,8000 1,6000 так (0,5 ≤ 0,8 ≤ 1) ні (1,6 > 1) кільце — так, ромб — ні
(0,1; 0,1) 0,1414 0,3000 ні (0,1414 < 0,5) так (0,3 ≤ 1) кільце — ні, ромб — так
(0; 0,9) 0,9000 0,9000 так так кільце — так, ромб — так
(0,9; 0,9) 1,2728 2,7000 ні (> 1) ні кільце — ні, ромб — ні
(0,5; 0) — межа кільця 0,5000 1,0000 так (точно на межі) так (точно на межі) кільце — так, ромб — так
(0,25; 0,5) — межа ромба 0,5590 1,0000 так так (точно на межі) кільце — так, ромб — так

Дві останні точки перевіряють саме граничні випадки: (0,5; 0) лежить на внутрішньому колі кільця, (0,25; 0,5) — на стороні ромба. Обидві зараховано як належні, що відповідає суцільним межам на рисунку варіанта і підтверджує коректність роботи допуску EPS.

8. Висновки