Как ускорить преобразование моего вектора Libsvm в std::vector‹float›?

Введение

У меня есть вектор libsvm вида:

{i_1:v_1; i_2:v_2;...; i_n:v_n}

Где i_j:v_j представляют соответственно индекс и значение. Если значение равно null, ему не будет присвоен индекс.

Моя цель — вычислить евклидово расстояние между двумя векторами libsvm. Для этого я должен преобразовать их в vector<float> того же размера. В следующем примере я покажу функцию, которую использовал для преобразования вектора libsvm в vector<float>.


Пример

Первый столбец имеет индекс = 2648 и значение = 0,408734, что означает, что все значения перед ним равны нулю.

LIBSVM VECTOR = 2648:0,408734;4157:0,609588;6087:0,593104;26747:0,331008


Исходный код

#include <vector>
#include <string>
#include <chrono>
#include <boost/algorithm/string.hpp>

using namespace std;
using namespace chrono;
//convert libsvm vector to float vector in order to compute the similarity
vector<float> splitVector(const vector<string> &);

int main()
{
   vector<string> libsvm {"2648:0.408734","4157:0.609588","6087:0.593104","26747:0.331008" };
   high_resolution_clock::time_point t1 = high_resolution_clock::now();
   vector<float> newVec = splitVector(libsvm);
   high_resolution_clock::time_point t2 = high_resolution_clock::now();
   auto duration = chrono::duration_cast<chrono::microseconds>( t2 - t1 ).count();
   cout <<"construction time: " << duration << endl;
   return 0;
}

vector<float> splitVector(const vector<string> & v)
{
    int numberofterms = 266373;
    vector<float> values;
    vector<int> previous_idx;
    for(int i = 0; i < v.size(); i++)
    {
        vector<string> tmpv;
        boost::split(tmpv, v[i] , boost::is_any_of(":"));
        //idx:value
        int idx = atoi(tmpv[0].c_str());
        float val = atof(tmpv[1].c_str());

        //summation of previous indices
        int sum = accumulate(previous_idx.begin(), previous_idx.end(), 0);
        int n = idx - (sum + i + 1);
        //fill vector with 0s
        for(int k = 0; k < n; k++)
            values.push_back(0.0);
        //add value
        values.push_back(val);
        previous_idx.push_back(n);
    }//end for

    int paddingsize = numberofterms - values.size();

    for(int i = 0; i < paddingsize;i++)
    {
      values.push_back(0.0);
    }
    return values;
}//end function

Проблема

Время преобразования составляет около 0,00866 секунд, и когда у меня есть около 1000 векторов, оно становится медленным. Есть ли более быстрый способ преобразовать вектор libsvm в vector<float>?


Модифицированная функция

values.resize(266373,0.0);
void splitVector(const vector<string> & v, vector<float> & values)
{
    vector<string> tmpv;
    for(int i = 0; i < v.size(); i++)
    {
        boost::split(tmpv, v[i] , boost::is_any_of(":"));
        //idx:value
        int idx = atoi(tmpv[0].c_str());
        float val = atof(tmpv[1].c_str());
        tmpv.clear();
        values[idx] = val;
    }//end for

}//end function

person Hani Goc    schedule 14.04.2015    source источник
comment
vector<float> newVec = plitVector(libsvm); ‹- афаик, это даже не должно компилироваться   -  person deW1    schedule 14.04.2015
comment
извините, я скопировал неправильные упсы   -  person Hani Goc    schedule 14.04.2015
comment
vector<int> libsvm {"2648:0.408734","4157:0.609588","6087:0.593104","26747:0.331008" }; вектор int со строковой инициализацией?   -  person NathanOliver    schedule 14.04.2015
comment
Повторно использовать tmpv; предварительно выделить values и previous_idx   -  person timrau    schedule 14.04.2015
comment
@timrau, если у меня есть более одного вектора libsvm, размер будет другим. Поэтому для previous_idx мне приходится предварительно выделять память каждый раз, когда я вхожу в функцию. Что касается значений, я думаю, да, это можно сделать, потому что размер вектора постоянен 266373. Я прав?   -  person Hani Goc    schedule 14.04.2015
comment
Вы все еще можете повторно использовать previous_idx, так как previous_idx.clear() на самом деле не освобождает память. Он просто сбрасывал размер вектора обратно на 0, а ранее выделенную память можно было повторно использовать до тех пор, пока емкости не станет недостаточно.   -  person timrau    schedule 14.04.2015
comment
И если values всегда имеет размер 266373, то вы можете использовать reserve(266373) в начале, чтобы выделить достаточно памяти, и использовать resize(266373, 0.0) в конце вместо многократного вызова push_back().   -  person timrau    schedule 14.04.2015
comment
@timrau Я написал функцию, в которой я предварительно выделяю, можете ли вы проверить это, пожалуйста? время стало 0,000109 лол   -  person Hani Goc    schedule 14.04.2015


Ответы (1)


Вы можете сократить затраты времени на выделение памяти, повторно используя vectors. Чтобы быть более конкретным,

  • Повторно используйте tmpv, объявив его перед циклом for и вызывая tmpv.clear() в начале каждого цикла.
  • Предварительно выделить values по values.reserve(); и дополните его values.resize(266373, 0.0) вместо повторяющегося push_back().
  • Повторно используйте previous_idx, если это возможно. Это может негативно сказаться на структуре кода и, следовательно, на ремонтопригодности.
person timrau    schedule 14.04.2015