Вычисление моментов высшего порядка в с ++ - PullRequest
0 голосов
/ 22 марта 2020

Я заинтересован в вычислении моментов высшего порядка некоторых наблюдаемых в программе на C ++. В настоящее время у меня есть рабочий метод, но он очень медленный, и моя реализация, вероятно, была наивной. Под «моментом» я подразумеваю только то, что обозначено здесь . У меня есть следующая функция, которая вычисляет момент до для усреднения (что я делаю «онлайн»),

vector<double> ExpVec(const vector<double>& Data, const double& momentOrder) {
    vector<double> ExpedVec(Data.size());
    for (size_t i = 0; i < Data.size(); ++i) 
        ExpedVec[i] = std::pow(Data[i], momentOrder);
    return ExpedVec;
}

Каждый элемент ExpedVec затем передается в объект, который вычисляет скользящее среднее. Таким образом, Data.size() - это количество выборок, и каждый раз, когда обнаруживается новый Data, мы пропускаем его через вышеупомянутое и добавляем новый материал к скользящему среднему. Процедура усреднения в основном то, что можно найти здесь здесь . Таким образом, используя класс RunningStats, мы имеем что-то вроде

int main() {
    uint nMoments = 5;
    double stepSize = 0.5; // Consider half integer moments too
    uint nSamples = 1000;
    vector<vector<RunningStats>> RS(nMoments, vector<RunningStats>(nSamples));

    for (uint n = 0; n < nSamples; ++n) {
        vector<double> Data = getData(n); // A vector of length M say
        for (size_t j = 0; j < nMoments; ++j) {
             vector<double> Exped = ExpVec(Data, (j+1) * stepSize);
             for (size_t k = 0; k < Exped.size(); ++k) {
                  RS[j][k].Push(Exped[j]); // Details aren't super important, it just stores the running average for each Data element
             }
        }
    }

}

Следует отметить, что да, схема RunningStats класса здесь уже вычисляет до момента порядка 4. Однако, если ваш общий размер выборки nSamples очень большой и вы можете вычислять подмножества общего количества выборок параллельно, у вас есть проблема с «объединением» ваших отклонений ». С другой стороны, когда мы вычисляем средние значения параллельно, мы имеем среднее значение средних значений, и нет необходимости объединять дисперсию , перекос и c.

в любом В случае, если детали main, я думаю, не так важны. Кажется, главная проблема с производительностью - это (используя ответ, обозначенный здесь ) pow функция. Это обсуждалось здесь и здесь и кажется, что если требуются нецелые степени, std::pow - это путь к go.

Если есть более подходящие способы решения этой проблемы go, и если у людей есть явный опыт решения проблемы, тогда я приветствую любые комментарии. С другой стороны, с чисто программной точки зрения c, что делать? Так много вложенных циклов, что я не уверен, как их избежать.

РЕДАКТИРОВАТЬ: Работоспособный пример ниже, с помощью комментариев:

#include <vector>
#include <iostream>
#include <math.h> 
#include <algorithm>
#include <random>
#include <iterator>
#include <functional>
#include <chrono>
#include <iomanip>

using namespace std;
using namespace std::chrono;

struct RunningAverage {
    public: 
        RunningAverage() : nElems(0), mean(0.0) {}
        void Push(const double& x) {
            nElems++;
            mean += (x - mean) / nElems;
        }
        double getMean() const { return mean; }
    private:
        double mean;
        int nElems;
};

void ExpVec(vector<double>& Vec, const double& momentOrder) {
    for (size_t i = 0; i < Vec.size(); ++i) 
        Vec[i] = std::pow(Vec[i], momentOrder);
}

void MultVecbyVec(vector<double>& Vec1, const vector<double>& Vec2) {
    for (size_t i = 0; i < Vec1.size(); ++i)
        Vec1[i] = Vec1[i] * Vec2[i];
}

int main() {
    uint nMoments = 20;
    uint nSamples = 1000000;    // No. times to collect data
    uint DataSize = 1;          // Not an actual use case
    double momentStep = 0.5;    // nMoments/momentStep = largest moment
    uniform_real_distribution<double> unif(0.0, 1.0); // Used for generating dummy data
    default_random_engine re;

    // With recycling
    high_resolution_clock::time_point t0 = high_resolution_clock::now();
    vector<vector<RunningAverage>> Moments1(nMoments, vector<RunningAverage>(DataSize)); 
    for (uint i = 0; i < nSamples; ++i) {
        vector<double> Data(DataSize);
        for (uint i = 0; i < DataSize; ++i)
            Data[i] = unif(re);                 

        vector<double> Accu = Data; 
        ExpVec(Accu, momentStep);  
        vector<double> Base = Accu;  
        for (uint j = 0; j < nMoments; ++j) {           
            for (uint k = 0; k < DataSize; ++k) 
                Moments1[j][k].Push(Accu[k]); 
            MultVecbyVec(Accu, Base);           
        }
    }
    high_resolution_clock::time_point t1 = high_resolution_clock::now();
    auto duration1 = duration_cast<milliseconds>(t1 - t0).count();

    // Without recycling
    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    vector<vector<RunningAverage>> Moments2(nMoments, vector<RunningAverage>(DataSize)); 
    for (uint i = 0; i < nSamples; ++i) {
        vector<double> Data(DataSize);
        for (uint i = 0; i < DataSize; ++i)
            Data[i] = unif(re);                 // Dummy data

        for (size_t j = 0; j < nMoments; ++j) {
            vector<double> Exped = Data;
            ExpVec(Exped, (j + 1.) * momentStep);
            for (size_t k = 0; k < Exped.size(); ++k) 
                Moments2[j][k].Push(Exped[k]);
        }

    }
    high_resolution_clock::time_point t3 = high_resolution_clock::now();
    auto duration2 = duration_cast<milliseconds>(t3 - t2).count();

    // Summary
    for (uint j = 0; j < nMoments; ++j)      
            for (uint k = 0; k < DataSize; ++k) 
                cout << "Moment order (" << fixed << setprecision(3) << (j + 1) * momentStep << "): With recycling = " << Moments1[j][k].getMean()
                     << ". Without recycling = " << Moments2[j][k].getMean() 
                     << ". Expected value = " << 1./(momentStep * (j + 1.) + 1.) << endl;
    cout << endl << "Run time with recycling: " << duration1 << endl;
    cout << "Run time without recycling: " << duration2 << endl;

    return 0;
}

Что дает выход:

Moment order (0.500): With recycling = 0.667. Without recycling = 0.667. Expected value = 0.667
Moment order (1.000): With recycling = 0.500. Without recycling = 0.500. Expected value = 0.500
Moment order (1.500): With recycling = 0.400. Without recycling = 0.400. Expected value = 0.400
Moment order (2.000): With recycling = 0.334. Without recycling = 0.333. Expected value = 0.333
.
.
.
Moment order (9.500): With recycling = 0.095. Without recycling = 0.095. Expected value = 0.095
Moment order (10.000): With recycling = 0.091. Without recycling = 0.091. Expected value = 0.091

Run time with recycling: 1056
Run time without recycling: 4124
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...