A collection of data in a row (or column) which may be summed together, multiplied by number, and having the same type (for instance real number, integer, complex number, float) is called vector. In programming, we tend to be familiar with array
. Either in mathematics or programming, vector is accessed using index. For example, assume we have a vector that comprise , , , and so forth. Alternatively,
The indexes indicate the location of data in the list. The notation of index could use the , , and so on, or using the number sequence like , , and so on. Our entire discussion will prefer number sequence because it has same fashion upon the array
in our C/C++ codes later on, except the index begin from zero.
In C way, we declare an array using pointer or bracket like this one,
int *v_pointer = calloc(3, sizeof(int));
int v_bracket[3];
Manipulating them can be done using bracket or dereferencing pointer.
// Using bracket
for (i = 0; i < 3; i++) {
v_pointer[i] = i; // the same for v_bracket
}
// Using dereferencing
for (i = 0; i < 3; i++) {
*(v_pointer + i) = i; // the same for v_bracket
}
In C++ it would be the same, but we’d better to use container
instead. There are a lot of container in C++ standard library, one of them is called vector
as well. If you familiar with C++ template, vector
is a template whose type need to be defined beforehand. Assume we are declaring vector
of integer,
std::vector<int> v(3);
Accessing each member can be done easily using at
or bracket as follows :
// Using at
for(i = 0; i < 3; i++) {
v.at(i) = i;
}
// Using bracket
for(i = 0; i < 3; i++) {
v[i] = i; // faster without checking
}
Vector can be further treated for multiplication, division, addition, and subtraction. For scalar, the operation is computed element-wise. For vector, it depends. Let’s take a look for scalar one. Suppose we will have scalar multiplication ,
int A = 2;
for (i = 0; i < 3; i++) {
v_pointer[i] *= A; // C array
v[i] *= A; // C++ vector
}
Scalar operation for the rest would be the same. For the vector operation, let’s mention about dot product.
Dot product is defined as an algebraic operation that takes two equal-length sequences of number and returns a single number, or notated as :
where denotes summation notation and is the dimension of vector space. For instance in three-dimensional space, the dot product of vectors [1, 3, -5] and [4, -2, -1] is:
Using the definition above, we implement dot product as :
// C way
int a[] = {1, 3, -5};
int b[] = {4, -2, -1};
int i, result = 0;
for (i = 0; i < 3; i++) {
result += a[i] * b[i];
}
// C++ way
std::vector<int> a = {1, 3, -5};
std::vector<int> b = {4, -2, -1};
int result = 0;
for (size_t i = 0; i < 3; i++) {
result += a[i] * b[i];
}
Okay, that’s for naive approach. Now let’s do it using Armadillo. But before that let’s make a convention while using armadillo, that we always include armadillo library and using namespace for arma as follows,
#include "armadillo"
using namespace arma;
so that we don’t write it over and over again in our discussion. While compiling, we need also add -larmadillo
. To be clear, if we have a example.cc
containing :
// example.cc
#include "armadillo"
using namespace arma;
int main() {
vec a = {1, 3, -5};
vec b = {4, -2, -1};
auto c = a.t() * b;
return 0;
}
to compile (in g++
for example) :
g++ -std=c++11 example.cc -larmadillo
As we can see, armadillo has wrapped the operation be more convenient. Unlike the former approach, all we need to do is adding *
(+
, -
, or \
) , between two vectors which is much more intuitive.
In example.cc
, we declare the vector by vec
syntax and initialize by value. vec
itself is armadillo derived typedef. In armadillo for vector, there are two basic type i.e. Col
for column vector and Row
for row vector. Both Col
and Row
are template whose type need to be defined. For convenience, Armadillo have defined common typedef :
Column vector | Row vector |
---|---|
vec = Col<double> |
rowvec = Row<double> |
dvec = Col<double> |
drowvec = Row<double> |
fvec = Col<float> |
frowvec = Row<float> |
cx_vec = Col<cx_double> |
cx_rowvec = Row<cx_double> |
cx_dvec = Col<cx_double> |
cx_drowvec = Row<cx_double> |
cx_fvec = Col<cx_float> |
cx_frowvec = Row<cx_float> |
uvec = Col<uword> |
urowvec = Row<uword> |
ivec = Col<sword> |
irowvec = Row<sword> |
Either Col
or Row
can be constructed in several ways, for simplicity I take Col
constructor, but it also applies for Row
.
Constructor | Note |
---|---|
Col<T> () |
void argument constructor |
Col<T> (n_elem) |
using number of element |
Col<T> (n_elem, fill_type) |
idem, specify initial value with fill_type |
Col<T> (size(X)) |
adopt other vector size |
Col<T> (size(X), fill_type) |
idem, specify initial value with fill_type |
Col<T> (vec) |
copying vector vec |
Col<T> (mat) |
copying matrix mat if the dimension is matched |
Col<T> (initializer_list) |
using curly bracket |
Col<T> (std::vector) |
copying std::vector |
Col<T> (vec, vec) |
construct complex vector using two real vector |
For extent, fill_type
mentioned above comprise :
Fill type | Note |
---|---|
fill::zeros |
set all elements to 0 |
fill::ones |
set all elements to 1 |
fill::eye |
set the main diagonal to 1 and off-diagonal elements to 0 |
fill::randu |
set each element to a uniform random value |
fill::randn |
set each element to a gaussian random value |
fill::none |
do not modify the elements |
The correctness of using them while operating each will follow the mathematic rule. The point is, for example, if we define two column vectors as in example.cc
and intend to multiply them, we need to transpose the first vector by calling a.t()
. In our previous implementation in C/C++ way, terms of column vector, row vector, transpose, and so on will be meaningless, because there are no clear difference between row and column vector. Thus we need to pay more attention to how math does it. Yet, we will agree that the terms really matter later on in matrix section.
No comments:
Post a Comment