Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

Исходные данные

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

Ход работы

1. Пусть функция представлена дискретной последовательностью значений . Тогда для любых трех последовательных значений функции с номерами справедливо

Для дискретного ряда значений функцию удобно ее производную представить в виде [1]

,        (1)

или

       (2)

Это так называемые формулы левых и правых разностей. В точках экстремума производная меняет знак, а значит, если i-я точка является точкой экстремума, то знаки выражений (1) и (2) будут различаться. Так как шаг постоянен, то из (1) и (2) получим

.        (3)

Выражение (3) и будет основой алгоритма решения поставленной задачи.

2. Следующим этапом является выбор подходящей структуры данных. Можно было бы выбрать контейнер std::vector [3 – 4]из стандартной библиотеки. Однако необходимую функциональность можно без труда реализовать самостоятельно, добавив при этом несколько полезных функций.

Пользовательская структура данных, инкапсулирующая массив должна [3]

    динамически распределять память под массив и очищать ее за ненадобностью; обеспечивать стандартный доступ к элементам массива в С-стиле; обеспечивать стандартный вывод в поток вывода в С++-стиле.

Такой класс удобно реализовать как шаблонный [3 – 4]. Его листинг приведен ниже

//simple_vector. h

#ifndef _SIMPLE_VECTOR_H

#define _SIMPLE_VECTOR_H

НЕ нашли? Не то? Что вы ищете?

#include <iostream>

#include <fstream>

#include <exception>

//Исключение при попытки обратится к пустому массиву

struct xInvalidAccess : public std::exception

{};

//Исключение при неправильном индексе элемента

struct xIndexOutOfRange : public std::exception

{};

//Тип данных для индексов и размеров

typedef unsigned int szint;

/**Шаблонный класс массив

*  Повторяет некоторую функциональность стандартного класса std::vector

*  Содержит ряд дополнительных возможностей, как то ввод элемента из консоли

*  и прочее

**/

template <class T>

class simple_vector

{

private:

       szint size;//размер

       szint capacity;//емкость

       T* data;//указатель на область памяти, где хранятся объекты массива

       /*

       * Метод для копирования данных из внешнего источника в данный объект

       */

       void copy_from(T* src, szint sz)

       {

               clear();

               resize(sz);

               if (src && sz > 0)

               {

                       for (szint i = 0; i < size; ++i)

                       {

                               data[i] = src[i];

                       }

               }

       }

       /*

       * Метод для увеличения емкости массива

       */

       void inc_capacity()

       {

               capacity += kIncrementSize;

               T* buff = new T[capacity];

               if (data)

               {

                       for (szint i = 0; i < size; ++i)

                               buff[i] = data[i];

                       delete[] data;

               }

               data = buff;

       }

public:

/*

       * Постоянная величина, на которую увеличивается емкость массива при заполнении

       */

       static szint kIncrementSize;

/*

       * Конструкторы

       */

       simple_vector()

               : size(0), capacity(0), data(NULL)

       {}

       

       simple_vector(T* src, szint sz)

       { copy_from(src, sz); }

       simple_vector(const simple_vector<T>& src)

       { copy_from(src. data, src. size); }

/*

       * Деструктор

       */

       ~simple_vector()

       { clear(); }

       simple_vector<T>& operator = (const simple_vector<T>& rhs)

       {

               if (&rhs!= this)

                       copy_from(rhs. data, rhs. size);

               

               return *this;

       }

       /*

       * Изменение размеров массива. Емкость и размер при этом совпадают

       */

       void resize(szint new_size)

       {

               if (size == new_size && new_size > 0)

                       return;

               T* buff = new T[new_size];

               

               if (data)

               {

                       for (szint i = 0; i < size && i < new_size; ++i)

                       {

                               buff[i] = data[i];

                       }

                       delete[] data;

               }

               data = buff;

               size = new_size;

               capacity = size;

       }

       /*

       * Очистка массива и освобождение занятой памяти

       */

       void clear()

       {

               if (data)

                       delete[] data;

               size = 0;

               capacity = 0;

       }

       /*

       * Перегруженные операторы доступа к элементам массива по индексу

       */

       T& operator [](szint index)

       {

               if (!data)

                       throw xInvalidAccess();

               

               if (index >= size)

                       throw xIndexOutOfRange();

               

               return data[index];

       }

       

       const T& operator [](szint index) const

       {

               if (!data)

                       throw xInvalidAccess();

               

               if (index >= size)

                       throw xIndexOutOfRange();

               return data[index];

       }

       /*

       * Оператор приведения к типу bool

       */

       operator bool () const

       {

               return data!= NULL;

       }

       /*

       * Метод, возвращающий текущий размер массива

       */

       szint get_size() const

       { return size; }

       /*

       * Метод, возвращающий емкость массива

       */

       szint get_capacity() const

       { return capacity; }

       /*

       * Метод для добавления элемента в массив

       * Возвращает индекс добавленного элемента

       */

       szint add(T item)

       {

               if (!size || size == capacity)

                       inc_capacity();

               data[size++] = item;

               return size - 1;

       }

       /*

       * Метод для поиска элемента в массиве

       * Возвращает индекс элемента или -1

       */

       int find(T item)

       {

               if (!data)

                       return -1;

               for (szint i = 0; i < size; ++i)

               {

                       if (data[i] == item)

                               return i;

               }

               

               return -1;

       }

       /*

       * Метод для удаления элемента с заданным индексом из массива

       */

       bool remove(szint index)

       {

               if (!data || !size)

                       return false;

               for (szint i = index; i < size - 1; ++i)

               {

                       data[i] = data[i + 1];

               }

               --size;

               return false;

       }

       /*

       * Метод, возвращающий признак пустоты массива

       */

       bool empty() const

       { return size == 0; }

       /*

       * Функция для вывода массива в поток вывода

       */

       template<class U>

       friend std::ostream& operator << (std::ostream& os, const simple_vector<U>& vector);

       /*

       * Метод для добавления элемента в массив

       */

       simple_vector<T>& operator << (const T& item)

       {

               add(item);

               return *this;

       }

};

template <class T>

szint simple_vector<T>::kIncrementSize = 16;

template<class T>

std::ostream& operator << (std::ostream& os, const simple_vector<T>& vector)

{

       for (szint i = 0; i < vector. get_size(); ++i)

       {

               if (i > 0)

                       os << " ";

               os << vector[i];

       }

       

       return os;

}

#endif

Листинг 1. Код класса, инкапсулирующего массив

В листинге 2 приведен код программы

#include <iostream>

#include <fstream>

#include <sstream>

#include <string>

#include <exception>

#include "simple_vector. h"

//функция для подсчета числа экстремумов

int count_extremums(const simple_vector<double>&);

//функция для преобразования строкового представления числа: 1.45*10^(-3) -> 1.45E-3

void transform_str_num(std::string&);

int main(int argc, char** argv)

{

       simple_vector<double> v;

       std::string file_name;

       //получаем имя файла данных из командной строки

       if (argc > 1)

               file_name = argv[0];

       else//или из консоли

       {

               std::cout << "Enter file name: ";

               std::cin >> file_name;

       }

       //открываем файл

       std::ifstream ifs(file_name. c_str());

       if (!ifs)

       {

               std::cout << "Error opening of file: " << file_name << std::endl;

               return -1;

       }

       

       std::string line;

       //считываем строки из файла

       while (std::getline(ifs, line))

       {

               std::stringstream sin;

               sin << line;//каждую строку как пару чисел передаем строковому потоку

               std::string y_str;

               sin >> y_str;//пропускаем Х

               sin >> y_str;//извлекаем Y

               //корректируем Y

               transform_str_num(y_str);

               double item;

               //передаем строку в строковый поток

               sin << y_str;

               //пытаемся извлечь Y в виде числа

               if (sin >> item)

               {

                       v << item;//если ОК, добавляем в массив

               }

       }

       ifs. close();

       std::cout << "Count of extremums: " << count_extremums(v) << std::endl;

       return 0;

}

void transform_str_num(std::string& str)

{

       const std::string subs = "*10^";

       std::size_t ipos = str. find(subs);

       if (ipos!= std::string::npos)

       {

               str. replace(ipos, subs. length(),"E");

       }

       ipos = str. find("(");

       if (ipos!= std::string::npos)

       {

               str. replace(ipos,1,"");

       }

       ipos = str. find(")");

       if (ipos!= std::string::npos)

       {

               str. replace(ipos,1,"");

       }

}

//шаблонная функция для вычисления знака числа

template <class T>

int sgn_number(const T& a)

{

       return (a < 0) ? -1 : (a > 0) ? 1 : 0;

}

int count_extremums(const simple_vector<double>& src)

{

       //Если массив не задан или задан неправильный его размер - выходим с кодом ошибки

       if (!src)

               return -1;

       int cn_extrems = 0;

       for (int i = 1; i < src. get_size() - 1; ++i)

       {

               //вычисляем знаки наклона касательной

               int sgn1 = sgn_number(src[i] - src[i - 1]);

               int sgn2 = sgn_number(src[i + 1] - src[i]);

               //если они разные - экстремум

               if (sgn1 != sgn2)

               {

                       std::cout << i + 1 << ": " << src[i] << std::endl;

                       ++cn_extrems;

               }

       }

       return cn_extrems;

}

Листинг 2. Код программы для поиска экстремумов.

В программе (см. листинг 2) сначала создается объект массива.

simple_vector<double> v;

Ввод исходных данных в него осуществляется из файла, имя которого задается из командой строки или из консоли

       std::string file_name;

       //получаем имя файла данных из командной строки

       if (argc > 1)

               file_name = argv[0];

       else//или из консоли

       {

               std::cout << "Enter file name: ";

               std::cin >> file_name;

       }

Затем пытаемся открыть файл. Если файл не существует, или произошла ошибка, то выходим из программы

       //открываем файл

       std::ifstream ifs(file_name. c_str());

       if (!ifs)

       {

               std::cout << "Error opening of file: " << file_name << std::endl;

               return -1;

       }

Далее идет процедура чтения файла. Поскольку тестовый файл имеет структуру

f(x)=x*x*sin(x)-x*cos(x)

x  f(x) 

…………………………………………………………….,

то его чтение в цикле осуществляется по такому алгоритму:

    считываем строку из файла в объект типа std::string

while (std::getline(ifs, line))

    передаем эту строку объекту типа std::stringstream

std::stringstream sin;

sin << line;//каждую строку как пару чисел передаем строковому потоку

    из объекта типа std::stringstream считываем сначала первый элемент данных (X) и не используем его

std::string y_str;

sin >> y_str;//пропускаем Х

    затем точно также считываем второй элемент данных (Y)

sin >> y_str;//извлекаем Y

поскольку строковое представление вещественного числа может содержать числа в формате 3.9801*10^(-5) и т. п, то предварительно корректируем такие числа, приводя их к виду 3.9801E-5

//корректируем Y

transform_str_num(y_str);

    затем опять передаем преобразованную строку в объект типа std::stringstream и пытаемся считывать уже оттуда число

double item;

//передаем строку в строковый поток

sin << y_str;

//пытаемся извлечь Y в виде числа

if (sin >> item)

{

v << item;//если ОК, добавляем в массив

}

    .

По завершению ввода данных закрываем файл с данными

       ifs. close();

и вызываем функцию для подсчета количества экстремумов

       std::cout << "Count of extremums: " << count_extremums(v) << std::endl;

Программа завершается корректно, так как не требуется ручной очистки памяти. Объект класса simple_vector удаляется при выходе из функции main, перед этим вызывается его деструктор, который и очищает всю занятую память.

Ниже приведен скриншот проекта Visual Studio 2012 (рис. 1).

Рисунок 1.

В качестве теста была рассмотрена функция

,

на отрезке . Шаг дискретизации был выбран 0,01. Протабулированную функцию было записано в файл test. txt.

На рисунке 2 представлены результаты работы программы

Рисунок 2.

На рис. 3 представлен график этой функции.

Рисунок 3.

Выводы

Разработано программу для поиска количества экстремумов дискретно заданной функции.

Получены навыки работы в среде Visual Studio 2012.

Получены навыки работы с шаблонными классами C++.

Литература

Вержбицкий методы (математический анализ и обыкновенные дифференциальные уравнения): Учебн. пособие для вузов. – М.: Высш. шк., 2001 Пратта, Стивен Язык программирования C++. Лекции и упражнения, 6 изд.: Пер. с англ. – М.: , 2012. Солтер, и др. C++ для профессионалов: Пер. с англ. – М.: , 2006. Шилдт, Герберт Искусство программирования на C++. – СпБ.: БХВ-Петербург, 2005.