Показаны сообщения с ярлыком CPP. Показать все сообщения
Показаны сообщения с ярлыком CPP. Показать все сообщения

воскресенье, 20 января 2019 г.

Таблично заданные функции и линейная интерполяция на C++

Время от времени в наших программах, при решении различных расчетных задач, возникает необходимость получать значения таблично заданных функций одной или двух переменных. Наиболее простым и надежным решением в данном случае является линейная интерполяция. В данной заметке я хочу привести пример определения значения таблично заданной функции двух переменных в виде реализации на C++.
В случаях, когда значения аргументов функции выходят за пределы таблицы, можно поступать по-разному: останавливать расчет и возвращать код ошибки или урезать значение аргумента до крайнего, представленного в таблице. В примере приведем первый вариант.
Стоит также учесть, что в зависимости от значений аргументов наш подход к работе с таблицей будет отличаться.
  1. Значения обоих аргументов совпадают со значениями сетки. В этом случае, не прибегая ни к каким вычислениям, мы выбираем значение функции из таблицы.
  2. Один из аргументов функции совпал со значением сетки. Тогда следует воспользоваться формулой линейной интерполяции для функции одной переменной.
  3. Ни один из аргументов функции не совпал со значениями сетки. Используем в таком случае формулы линейной интерполяции для функции двух переменных, что, собственно, не сильно отличается от работы по п.2 - то же самое, только в два захода.
Хорошо, теперь, наконец, переходим к коду. Пусть наша таблица определена несколькими векторами, содержащими значения сетки по оси X, значения сетки по оси Y и сами значения функции, то есть Z.
const vector<double> X = { 1, 1.5, 2, 2.5 };
const vector<double> Y = { 25, 30, 40 };
const vector< vector<double> > Z =
{ {0.392, 0.476, 0.574, 0.658},
  {0.406, 0.588, 0.770, 1.050},
  {0.434, 0.700, 0.980, 1.119} };
Затем приведем функции линейной интерполяции. В случае 3D, для пущей наглядности, я привожу в коде схему обозначения точек.
double linInterp2D(
    double x1,  double y1,
    double x2,  double y2,
    double x
    ) {

    double y = y1 + (x - x1) * (y2 - y1) / (x2 - x1);

    return y;
}

double linInterp3D(
    double x1,  double y1,
    double x2,  double y2,
    double z11, double z21, double z12, double z22,
    double x,   double y
    ) {

    /*

         |
      y2-| z12       z22
         |
       y-|      z
         |
      y1-| z11       z21
         |_______________
           |    |    |
           x1   x    x2

    */

    double z1 = z11 + (z12 - z11) * (y - y1) / (y2 - y1);
    double z2 = z21 + (z22 - z21) * (y - y1) / (y2 - y1);
    double z = z1 + (z2 - z1) * (x - x1) / (x2 - x1);

    return z;
}
И, наконец, нахождение значения таблично заданной функции по заданным x и y.
double findZ(double x, double y) {

    double x1 = 0; double x1i = 0;
    double y1 = 0; double y1i = 0;
    double x2 = 0; double x2i = 0;
    double y2 = 0; double y2i = 0;
    double z11 = 0;
    double z21 = 0;
    double z12 = 0;
    double z22 = 0;

    bool x_inside_range = false;
    bool y_inside_range = false;

    for (size_t i=0; i<X.size()-1; i++) {

        if ( x == X[i] ) {
            x1 = x2 = X[i];
            x1i = x2i = i;
            x_inside_range = true;
            break;
        }
        else if ( (x > X[i]) && (x < X[i+1]) ) {
            x1 = X[i];
            x1i = i;
            x2 = X[i+1];
            x2i = i+1;
            x_inside_range = true;
            break;
        }
        else if ( x == X[i+1] ) {
            x1 = x2 = X[i+1];
            x1i = x2i = i+1;
            x_inside_range = true;
            break;
        }
    }

    if ( !x_inside_range ) {
        cout << "x is outside of X axis range!\n";
        return -666;
    }

    for (size_t i=0; i<Y.size()-1; i++) {

        if ( y == Y[i] ) {
            y1 = y2 = Y[i];
            y1i = y2i = i;
            y_inside_range = true;
            break;
        }
        else if ( (y > Y[i]) && (y < Y[i+1]) ) {
            y1 = Y[i];
            y1i = i;
            y2 = Y[i+1];
            y2i = i+1;
            y_inside_range = true;
            break;
        }
        else if ( y == Y[i+1] ) {
            y1 = y2 = Y[i+1];
            y1i = y2i = i+1;
            y_inside_range = true;
            break;
        }
    }

    if ( !y_inside_range ) {
        cout << "y is outside of Y axis range!\n";
        return -666;
    }

    z11 = Z[y1i][x1i];
    z21 = Z[y1i][x2i];
    z12 = Z[y2i][x1i];
    z22 = Z[y2i][x2i];

    if ( (x1 == x2) && (y1 == y2) ) {
        return Z[y1i][x1i];
    }
    else if ( (x1 == x2) && (y1 != y2) ) {
        return linInterp2D(y1, z11, y2, z12, y);
    }
    else if ( (x1 != x2) && (y1 == y2) ) {
        return linInterp2D(x1, z11, x2, z21, x);
    }
    else {
        return linInterp3D(x1, y1, x2, y2, z11, z21, z12, z22, x, y);
    }
}
Вот и все. Вроде бы совершенно ничего сложного, но такие вот заготовочки весьма полезны, когда сроки поджимают, а некоторые функции уже написаны. Это здорово экономит время.

воскресенье, 1 декабря 2013 г.

C++. Intel hex. Checksum calculation

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>
#include <cstdint>

using namespace std;

vector<size_t> hexToNumBS(const string &srcStr) {

    vector<size_t> v;

    if ( srcStr.size() < 2 ) {
        return v;
    }

    stringstream ss;
    size_t code = 0;

    for ( size_t i=1; i<srcStr.size(); i+=2 ) {

        ss << hex << srcStr.substr(i-1, 2);
        ss >> code;
        v.push_back(code);

        ss.clear();
    }

    return v;
}

string checksum(const string &str) {

    vector<size_t> v = hexToNumBS(str);
    u_int8_t cs = 0;

    for ( size_t i=0; i<v.size(); i++ ) {
        cs -= v[i];
    }

    stringstream ss;
    ss << hex << static_cast<size_t>(cs);

    string ret;
    ss >> ret;

    if ( ret.size() < 2 ) {
        ret = "0" + ret;
    }
    else if ( ret.size() > 2 ) {
        return "";
    }

    transform(ret.begin(), ret.end(), ret.begin(), ::toupper);

    return ret;
}

int main() {

    string fullOrigStr("102AE0007C0803A6382100084E8000209421FFE8CE");
    cout << "Full original string:              "
         << fullOrigStr << "\n";

    string origStr = fullOrigStr.substr(0, fullOrigStr.size()-2);
    cout << "Original string w/o checksum byte: " << origStr << "\n";

    cout << "Calculated checksum:               "
         << checksum(origStr) << "\n";

    return 0;
}

четверг, 21 ноября 2013 г.

C++. Convert string to hex

После вчерашней заметки C++. Convert hex to string, стоит привести пример решения обратной задачи. То бишь, сконвертировать string в hex.
#include <iostream>
#include <sstream>

using namespace std;

void stringToHex(const string &srcStr, string &destStr) {

    destStr.clear();

    stringstream ss;

    for ( size_t i=0; i<srcStr.size(); i++ ) {
        ss << hex << static_cast<size_t>(srcStr[i]);
    }

    ss >> destStr;
}

int main() {

    string srcstr("Hello world!");
    string deststr;

    stringToHex(srcstr, deststr);

    cout << "\n" << deststr << "\n\n";

    //

    return 0;
}

среда, 20 ноября 2013 г.

C++. Convert hex to string

В рамках одного проекта встала задача сконвертировать hex в string. Гуглятся совершенно разные примеры и подходы. Я же хочу поделиться своим, на мой взгляд, наиболее простым и ясным решением.
#include <iostream>
#include <sstream>

using namespace std;

void hexToString(const string &srcStr, string &destStr) {

    if ( srcStr.size() < 2 ) {
        return;
    }

    destStr.clear();

    stringstream ss;
    size_t asciiCode = 0;

    for ( size_t i=1; i<srcStr.size(); i+=2 ) {

        ss << hex << srcStr.substr(i-1, 2);
        ss >> asciiCode;

        destStr.push_back(static_cast<char>(asciiCode));

        ss.clear();
    }
}

int main() {

    string srcstr("48656C6C6F20776F726C6421");
    string deststr;

    hexToString(srcstr, deststr);

    cout << "\n" << deststr << "\n\n";

    //

    return 0;
}

суббота, 22 июня 2013 г.

Bjarne Stroustrup. The C++ Programming Language (4th Edition)

Создатель языка C++ Бьерн Страуструп работает над 4-м изданием книги "Язык программирования C++". С черновиками к первым главам можно ознакомиться по ссылке http://www.stroustrup.com/4th.html.

понедельник, 3 сентября 2012 г.

Книги по C++

Сегодня один мой товарищ, решивший начать изучение этого замечательного языка программирования, задал мне вопрос - какие книги по C++ нужно иметь в своей библиотеке? Подобные вопросы также неоднократно задаются на различных форумах соответствующей тематики. В связи с этим, я решил представить список книг, имеющихся в моей библиотеке. Разумеется, это не все, но перечисленное ниже, на мой взгляд, нужно иметь, а еще лучше изучить и переварить )))
  1. Бьерн Страуструп. Язык программирования C++. Специальное издание. 2011;
  2. Стивен Прата. Язык программирования C++. Лекции и упражнения. 5-е издание. 2007;
  3. Николай Джосьютис. C++. Стандартная библиотека. 2004;
  4. Брюс Эккель. Философия C++. Введение в стандартный C++. 2-е издание. 2004;
  5. Брюс Эккель, Чак Эллисон. Философия C++. Практическое программирование. 2004;
  6. Герб Саттер. Решение сложных задач на C++. 2002;
  7. Герб Саттер. Новые сложные задачи на C++. 2005;
  8. Стефан Дьюхэрст. Скользкие места C++. 2006;
  9. Мэтью Уилсон. C++. Практический подход к решению проблем программирования. 2006;
  10. Д. Райан Стефенс, Кристофер Диггинс, Джонатан Турканис, Джефф Когсуэлл. C++. Сборник рецептов. 2007.
Не забывайте о стандарте! Ссылки на документы можно найти в разделе Ссылки.

Рекомендация для начинающих - начните работать с языком, вооружившись только компилятором (рекомендую gcc) и текстовым редактором (тут уж сами смотрите, что удобней), что позволит вам лучше сконцентрироваться на главном и не отвлекаться. Понравившуюся IDE ничто не мешает освоить позже.

Удачи!

UPD 25.11.2012

Добавка - великолепная книга, которую я почему-то упустил:
  • Скотт Мэйерс. Эффективное использование C++. Третье издание. 55 верных советов улучшить структуру и код ваших программ. 2006.

четверг, 16 февраля 2012 г.

std::vector. Освобождение памяти

Собственно решение.
vector<double> v;

// какая-то работа с вектором

// освобождение памяти, занимаемой вектором
v.clear();
vector<double>().swap(v);

C++. Чтение CSV файла в двумерный массив

В качестве примера использования предложенных ранее функций разбивки строки и конвертирования строки в число привожу листинг программы, которая читает данные из файла формата CSV в двумерный массив и выводит полученное на консоль.
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <iostream>

using std::string;
using std::vector;
using std::ifstream;
using std::istringstream;
using std::cout;

void splitString(const string &fullstr,
                 vector<string> &elements,
                 const string &delimiter) {

    string::size_type lastpos =
        fullstr.find_first_not_of(delimiter, 0);
    string::size_type pos     =
        fullstr.find_first_of(delimiter, lastpos);

    while ( (string::npos != pos) || (string::npos != lastpos) ) {

        elements.push_back(fullstr.substr(lastpos, pos-lastpos));

        lastpos = fullstr.find_first_not_of(delimiter, pos);
        pos = fullstr.find_first_of(delimiter, lastpos);
    }
}

double stringToDouble(const string &str) {

    istringstream stm;
    double val = 0;

    stm.str(str);
    stm >> val;

    return val;
}

void readData(const string &filename,
              const string &csvdelimiter,
              vector< vector<double> > &sarr) {

    ifstream fin(filename.c_str());

    string s;
    vector<string> selements;
    vector<double> delements;

    while ( !fin.eof() ) {

        getline(fin, s);

        if ( !s.empty() ) {

            splitString(s, selements, csvdelimiter);

            for ( size_t i=0; i<selements.size(); i++ ) {

                delements.
                    push_back(stringToDouble(selements[i]));
            }

            sarr.push_back(delements);
            selements.clear();
            delements.clear();
        }
    }

    fin.close();
}

int main(int argc, char** argv) {

    vector< vector<double> > sarr;
    
    readData("data.csv", ";", sarr);

    for ( size_t i=0; i<sarr.size(); i++ ) {

        for ( size_t j=0; j<sarr[0].size(); j++ ) {

            cout << sarr[i][j] << "\t";
        }

        cout << "\n";
    }

    return 0;
}
Дабы не раздувать приведенный в качестве примера код, проверка доступности файла, возможности его открытия, а также однородности массива в файле с данными были опущены. Не забывайте о подобных проверках в реальных программах!

среда, 15 февраля 2012 г.

C++. Замена в строке

Решением может быть такая функция:
#include <string>

using std::string;

...

void replaceInString(string &str,
                     const string &str1,
                     const string &str2) {

    size_t n = 0;

    while ( (n = str.find(str1)) != string::npos ) {

        str.replace(n, str1.length(), str2);
    }
}

...
UPD 25.02.2015
Хотя можно просто воспользоваться std::replace. Приведем пример с заменой запятой на точку.
#include <algorithm>
...
using std::replace;
...
replace(str.begin(), str.end(), ',', '.');
...

C++. Разбивка строки

Решением может быть такая функция:
#include <string>
#include <vector>

using std::string;
using std::vector;

...

void splitString(const string &fullstr,
                 vector<string> &elements,
                 const string &delimiter) {

    string::size_type lastpos =
        fullstr.find_first_not_of(delimiter, 0);
    string::size_type pos     =
        fullstr.find_first_of(delimiter, lastpos);

    while ( (string::npos != pos) || (string::npos != lastpos) ) {

        elements.push_back(fullstr.substr(lastpos, pos-lastpos));

        lastpos = fullstr.find_first_not_of(delimiter, pos);
        pos = fullstr.find_first_of(delimiter, lastpos);
    }
}

...

C++. Конвертирование числа в строку

Решением может быть функция:
#include <string>
#include <sstream>

using std::string;
using std::ostringstream;

...

string numberToString(double x) {

    ostringstream stm;
    stm << x;

    return stm.str();
}

...

UPD 06.01.2021
Давно следовало дописать, что стандарт C++11 подарил нам такую функцию как std::to_string.

C++. Конвертирование строки в число

Решением может быть такая функция:
#include <string>
#include <sstream>

using std::string;
using std::istringstream;

...

double stringToDouble(const string &str) {

    istringstream stm;
    double val = 0;

    stm.str(str);
    stm >> val;

    return val;
}

...


UPD 06.01.2021
Новые функции в C++11.

Повседневные вопросы и их решения на C++

Планирую начать публикацию коротких заметок с решениями различных, скажем так, подзадач, с которыми может столкнуться инженер, при использовании C++ в своей работе - при создании расчетных приложений и обработке экспериментальных данных. Надеюсь, что кому-нибудь найденные мной решения пригодятся, а в комментариях, возможно, будут предложены более эффективные способы. Будем делиться опытом )

понедельник, 13 февраля 2012 г.

std vector iterator

Стало интересно, насколько много мы теряем, когда с дуру и не к месту перебираем элементы вектора не по индексу, а используя итератор.

Вариант 1 - доступ к значениям элементов вектора по индексу.
#include <iostream>
#include <vector>

using namespace std;

int main() {

    size_t vsize = 200000000;

    vector<double> v;
    v.resize(vsize, 0);

    for ( size_t i=0; i<vsize; i++ ) {

        v[i] = i/0.23;
    }

    double x = 0;

    for ( size_t i=0; i<vsize; i++ ) {

        x = v[i] / 283746.40596837 * 0.92386936827364;
    }

    return 0;
}
Среднее по 5-ти замерам время работы программы составило 4.7136 сек.

Вариант 2 - доступ к значениям элементов вектора с использованием итератора.
#include <iostream>
#include <vector>

using namespace std;

int main() {

    size_t vsize = 200000000;

    vector<double> v;
    v.resize(vsize, 0);

    for ( size_t i=0; i<vsize; i++ ) {

        v[i] = i/0.23;
    }

    double x = 0;

    for ( vector<double>::iterator it=v.begin(); it!=v.end(); ++it ) {

        x = *it / 283746.40596837 * 0.92386936827364;
    }

    return 0;
}
Среднее по 5-ти замерам время работы программы составило 7.196 сек.

Итак, производительность во втором случае упала почти на 34.5%... Честно говоря, я не думал, что итераторы настолько круто снижают производительность.
Для справки: компиляция осуществлялась с флагом -O0.

UPD: 09.01.2013

А теперь вариант 3 (с использованием возможностей C++11).
#include <iostream>
#include <vector>

using namespace std;

int main() {

    size_t vsize = 200000000;

    vector<double> v;
    v.resize(vsize, 0);

    for ( size_t i=0; i<vsize; i++ ) {

        v[i] = i/0.23;
    }

    double x = 0;

    for ( size_t z : v ) {

        x = z / 283746.40596837 * 0.92386936827364;
    }

    return 0;
}
Производительность практически та же, что и в варианте 2.

среда, 1 февраля 2012 г.

Распараллеливание сборки проекта

Для повышения эффективности сборки проекта путем ее распараллеливания необходимо запускать make с ключом -jX, где X - количество процессоров в системе плюс один.
В Qt Creator это указывается в разделе Проекты -> Этапы сборки -> Сборка -> Подробнее -> Параметры make.
Говорят, что в виндовсах этого мало и нужно еще использовать ключ -f Makefile.Release (или Makefile.Debug).

Подробности см. здесь.

воскресенье, 30 октября 2011 г.

Новые возможности C++11

Очередной весьма интересный и полезный обзор новых возможностей C++11. В этот раз от Герба Саттера. В статье приводятся примеры с комментариями, а также сравнение различных реализаций, произведенных средствами С++03 и С++11.

http://herbsutter.com/elements-of-modern-c-style/

пятница, 24 июня 2011 г.

STL и QTL

На днях мне приспичило сравнить производительность std::vector и QVector при их простейшем использовании, а сегодня на глаза попалась хорошая заметка - сравнение STL и QTL.
Вообще, в консольных приложения я использую только STL, однако, при создании кросс платформенных приложений с графическим интерфейсом, Qt сильно облегчает жизнь.

четверг, 23 июня 2011 г.

std::vector и QVector

А что мы, собственно, теряем по скорости используя QVector вместо sdt::vector? Оказывается не так уж и мало...

Машина и ПО, использовавшиеся в тесте:


Код с использованием std::vector
#include <vector>

using std::vector;

int main(int argc, char *argv[]) {

    vector<double> v;
    v.resize(10000000);

    for (size_t i=0; i<v.size(); i++) {

        v[i] = 67.687353 * (i + 13.507867) /
        (i * 45.686754) - 12.56;
    }

    return 0;
}

Код с использованием QVector
#include <QVector>

int main(int argc, char *argv[]) {

    QVector<double> v;
    v.resize(10000000);

    for (int i=0; i<v.size(); i++) {

        v[i] = 67.687353 * (i + 13.507867) /
        (i * 45.686754) - 12.56;
    }

    return 0;
}

Замеры времени выполнения программы проводились с помощью утилиты time.

Результаты: в среднем, (по пяти замерам) использование QVector увеличило время выполнения программы на 10%...

среда, 22 июня 2011 г.

g++ -Ox...

Один программист провел интересное исследование: сравнение эффективности одного и того же простого действия при использовании глобальной функции, inline глобальной функции, чистого функтора, лямбда-функции C++11 и лямбда-функции Boost.Phoenix. Полученные результаты оказались весьма интересны, в особенности то, насколько на таблицу результатов повлиял флаг g++ -Ox. Итак, при -O0 в лидеры вышел код с использованием лямбда-функции C++11, а при использовании -O3 лямбда-функция C++11 оказалась на последнем месте! В лидерах при максимальной оптимизации глобальная функция, чистый функтор и лямбда-функция Boost.Phoenix. Вот так.

Более подробно - см. первоисточник.