Click to See Complete Forum and Search --> : linear combination of 2 std::vectors


ejac
April 14th, 2008, 09:19 AM
I was wondering: is there a (more elegant, and preferably more efficient) way to do this:

vector1[i] = a*vector2[i] + (1-a)*vector3[i];

for every element of the vector without looping through the vectors myself. (with vectors of floats)

laserlight
April 14th, 2008, 09:30 AM
You can use std::transform to combine two ranges into one by applying a binary (as in dyadic) function.

GNiewerth
April 14th, 2008, 09:49 AM
std::transform surely works, but in combination with binders itīs almost unreadable and far from elegant. If you have to deal with those operations I strongly recommend a dedicated library such as Blitz++ or the Matrix Template Library (MTL).

Lindley
April 14th, 2008, 10:17 AM
std::vector is not ideal for mathematical vectors. Find another library.

ejac
April 14th, 2008, 12:12 PM
Thanks for the advice, I have little experience using std algorithms and functions objects, so even if it doesn't make things better, for me its still a learning opportunity.

I am inheriting from an existing class using a vector of floats, so i cant really use anything else easily, as that vector is the result vector for the operation.

Also i didn't know about Blitz++ or MTL, certainly interesting libs, i'll bookmark them for later. (I have once implemented 4x4 matrix inversion because i couldnt find an easy to use, free lib that did that for me)

This is what i came up with, and it seems to work. I made a function object because I couldnt figure out how to do it with binders. Also I think it makes readability a little better, and stroustrup says: "function objects often execute faster than do ordinary functions" (18.4 in the c++ programming language 3rd edition). The linear combination im doing is for crossfading between the numerators of a filter, hence the naming:

the function object:
class CrossFade
{
public:
CrossFade(float crossFadeAmount=0):m_cfa(crossFadeAmount){}

void setCrossFadeAmount(float crossFadeAmount)
{
m_cfa = crossFadeAmount;
}

float getCrossFadeAmount() const {return m_cfa;}

float operator()(float start, float target) const
{
return (1-m_cfa)*start + m_cfa*target;
}

private:
float m_cfa;
};


and the (pruned) calling class + code is as follows:

class EFilter :
public Filter
{
public:
//...


private:
float m_crossfadeAmount;
std::vector<float> m_targetNum;
std::vector<float> m_startNum;
//crossfade function object
CrossFade m_crossFader;

void doCrossFade();
};


void EFilter::doCrossFade()
{
m_crossFader.setCrossFadeAmount(m_crossfadeAmount);
//b_ is an inherited vector of floats
//I should check the sizes of the vectors
transform(m_startNum.begin(),m_startNum.end(),m_targetNum.begin(),b_.begin(),m_crossFader);
}

laserlight
April 14th, 2008, 12:28 PM
To be const-correct getCrossFadeAmount and operator() should be const member functions.

JamesSchumacher
April 14th, 2008, 05:26 PM
I was wondering: is there a (more elegant, and preferably more efficient) way to do this:

vector1[i] = a*vector2[i] + (1-a)*vector3[i];

for every element of the vector without looping through the vectors myself. (with vectors of floats)

Why do you have to not loop through it yourself? Do you do more operations per each index than just this? If so, use a reference inside the loops to denote which entry you are referring to.


std::vector<MyDataStruct> arDataVector;

// initialize data into vector somewhere here

for (std::size_t x = 0; x < 100; ++x)
{
MyDataStruct & objRef = arDataVector[x];

objRef.DoSomething();
objRef.DoSomethingElse();
}

Philip Nicoletti
April 14th, 2008, 07:42 PM
There is a STL class named valarray which does what you want,
but I have never used it and I doubt that the compiler writers
have put a lot of work optimizing it. Also, I think that they
are fixed length.


#include <valarray>

using namespace std;

int main( )
{
valarray<float> v2(10);
valarray<float> v3(10);

for (int i=0; i<10; ++i)
{
v2[i] = i;
v3[i] = i * i;
}

float a = 0.3f;

valarray<float> v1 = a*v2 + (1-a)*v3;

v2 += v3;

v3 = 1.0f;

return 0;
}