How to count instances of matrices in a vector? - c++

I'm trying to count instances of matrices using the following:
// setup some matrices to count instances of
std::vector<std::vector<int>> matrix1({{1, 2, 3},
{4, 5, 6}});
std::vector<std::vector<int>> matrix2({{1, 2, 3},
{4, 5, 6}});
std::vector<std::vector<int>> matrix3({{7, 8, 9},
{10, 11, 12}});
std::vector<std::vector<int>> matrix4({{13, 14, 15},
{16, 17, 18}});
// collect matrices into a vector
std::vector<std::vector<std::vector<int>>> vector_of_matrices({matrix1, matrix2, matrix3, matrix4});
assert(4 == vector_of_matrices.size());
// convert into a set, so that we know how many unique elements there are
std::set<std::vector<std::vector<int>>> s(vector_of_matrices.begin(), vector_of_matrices.end());
assert(3 == s.size());
// store the counts, index wise
std::vector<int> counts_;
counts_.reserve(s.size());
// handle to set iterator
auto setIt = s.begin();
// iterate over number of unique elements in vector_of_matrices
for (int i=0; i < s.size(); i++){
// count instances of current set setIt
counts_[i] = std::count(vector_of_matrices.begin(), vector_of_matrices.end(), *setIt);
// increment set
std::advance(setIt, 1);
}
This code segfaults on the line that calls std::count. Does anybody have a suggestion to get this code to work? The expected output would be a std::vector<int> containing 3 elements, a 2 and two 1s.

From the cppreference page on reserve():
Increase the capacity of the vector to a value that's greater or equal to new_cap. If new_cap is greater than the current capacity(), new storage is allocated, otherwise the method does nothing.
reserve() does not change the size of the vector.
(emphasis mine)
So your counts_ vector actually has size 0, which means count_[i] = ... is undefined behavior.
Instead of calling reserve, you can just use the std::vector constructor to set an initial size:
std::vector<int> counts_(s.size());

Related

What is more efficient? vector.assign vs vector.erase

When a vector exists and tries to erase the back of that vector.
Is it efficient to use an 'vector.assign' in terms of time complexity? Or is it efficient to use 'vector.erase'?
Please let me know the time complexity in each case.
[For example]
vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 1. use assign
v.assign(v.begin(), v.begin() + 5);
// 2. use erase
v.erase(v.begin() + 5, v.end());
I would like to use vectors that consist only of elements from the beginning section of vectors to a certain index.
It's most efficient to use resize, because that's what that function is for.
For what its worth, self-assignment is not allowed for containers.

Find which subvector in a nested vector contains a certain item

For a nested vector, I want to have a function that returns the index of the subvector that contains a certain item. For example, given this nested vector:
vector<vector<int>> vec {{1, 2, 3},
{4, 5, 6},
{7, 8, 9, 10}};
I want to have a function that acts like so:
find_subvector(vec, 5); //returns 1 because V[1] contains the element 5.
All of the items among all of the subvectors are unique.
I do have a solution currently, which is to iterate over all of the subvectors and use std::find():
int find_subvector(const vector<vector<int>>& vec, int num) {
for (int i {}; i < (int)vec.size(); i++)
if (find(vec[i].begin(), vec[i].end(), num) != vec[i].end())
return i;
return -1;
}
But I wanted to know if there was a faster solution, because I will be running this function repeatedly.

How can I improve the following lambda function for finding the first 4 elements in a vector

For practice, I am trying to copy the first 4 entries different than 2 from a vector of integers using copy_if.
This seems to work but if there is a better way of writing this lambda then I'd like to learn the proper way. Cheers.
vector<int> first_vector = {2,8,50,2,4,5,9,12};
vector<int> second_vector (first_vector.size());
int count_elem=0;
auto it = copy_if(first_vector.begin(),first_vector.end(),second_vector.begin(),
[&count_elem]
(int i){
if(i!=2 && count_elem!=4)
{
count_elem++;
return 1;
}
return 0;});
Since you are not copying all of the values from first_vector to second_vector, you should not initialize second_vector to hold the same number of elements as first_vector. You are creating more elements than you want, where the extra elements are value-initialized to 0.
I would suggest reserve()'ing the size of second_vector instead and then use std::back_inserter as the destination iterator to copy to. That way, second_vector ends up with only the values you want pushed and nothing else.
That would also eliminate the need for the count_elem variable. You can use second_vector.size() to know how many values have been pushed into the vector.
std::vector<int> first_vector = {2, 8, 50, 2, 4, 5, 9, 12};
std::vector<int> second_vector;
second_vector.reserve(4);
std::copy_if(
first_vector.begin(), first_vector.end(),
std::back_inserter(second_vector),
[&](int i){
return ((i != 2) && (second_vector.size() < 4));
}
);
Do note, however, that this use of std::copy_if() will iterate through the entire first_vector, it will not stop iterating once 4 values have been pushed to second_vector. It would be more efficient to simply run your own loop instead so you can break it as soon as possible:
std::vector<int> first_vector = {2, 8, 50, 2, 4, 5, 9, 12};
std::vector<int> second_vector;
second_vector.reserve(4);
for(int i : first_vector) {
if (i != 2) {
second_vector.push_back(i);
if (second_vector.size() == 4)
break;
}
}

Why does using push_back on a vector declared with size result in a vector of zeros?

I made a vector of constant size to store negative values, and then printing the values all I got was zeroes. I just want to know why it is not storing negative values.
#include <iostream>
#include <vector>
int main() {
std::vector<int> v(5);
v.push_back(-1);
v.push_back(-2);
v.push_back(-3);
v.push_back(-4);
v.push_back(-5);
for (int i=0; i<5; i++)
std::cout << v[i] << " "; // All I got was zeroes
}
That's because push_back puts new elements onto the end of the vector.
You can see the effect by running i to 9: the negative numbers will occupy v[5] to v[9].
Writing
std::vector<int> v{-1, -2, -3, -4, -5};
instead is a particularly elegant fix.
The constructor that you invoke fills the first 5 elements with zeros, see here (#3 in the list of overloads):
Constructs the container with count default-inserted instances of T
(where the "default-inserted instance" of an int is 0). What you might have wanted is
std::vector<int> v;
v.reserve(5); /* Prevent unnecessary allocations as you know the desired size. */
v.push_back(-1);
/* ... */
An alternative using the original constructor call is
#include <numeric>
std::vector<int> v(5);
std::iota(v.rbegin(), v.rend(), -v.size());
though this does more work than necessary as every element is first default constructed and then assigned to a new value again.
This is a case where the DRY principle would help you understand your mistake.
vector<int> v(5);
...
for(int i=0;i<5;i++)
Here you are creating an array, for which you think you reserve space for 5 elements. Then you insert those 5 elements. After that you wanted to print contents of the whole array, but instead of just writing v.size(), you repeated the 5, so that your code now reads like "Print first five elements of v", instead of "Print all elements of v".
If you instead wrote what you mean, you'd see that the array actually has 10 elements, not 5.
BTW, since C++11 you can loop over all elements in a more straightforward way:
for(int x : v)
or, if the elements were some more copy-expensive type, you could use references to the elements, even auto-type references:
for(auto& x : v)
This new for-loop syntax is called the range-based for loop.
You can consider the vector a flexible version of the primitive array in C/C++. When you initialize a vector with a size n, the constructed vector has size of n (or maybe larger in the memory, but you don't know since it's implicitly handled by compiler). Note that here n represents the number of entries, but not the actual memory usage (i.e. bytes). If you do not initialize it with a size parameter, the vector is empty with size 0, but in the memory it would have some implicit default memory size.
Let's say your current vector has size 5. And you want to push_back() in another element, then the vector internally will reallocate the entire array into a new memory location which could hold all its old entries plus the new one. So you don't need to reallocate the memory manually by yourself, like what you have to do in C.
Here, in your example, to fill in those 5 negative integers in your vector, there are a couple of ways.
1) You can initialize a vector without specifying the size. And then push in each element you want.
vector<int> v;
for (int i = -1; i >= -5; --i) {
v.push_back(i);
}
2) You can initialize the vector in your way with that size parameter. And then assign them with new values.
vector<int> v(5);
for (int i = 0; i < v.size(); ++i) {
v[i] = -i;
}
3) You can also initialize the vector with those entries when it is constructed.
vector<int> v{-1, -2, -3, -4, -5};
or vector<int> v = {-1, -2, -3, -4, -5};
When you declared the vector with
std::vector<int> v(5);
You made v store five 4-byte spaces in memory (assuming an int = 4 bytes on your system), and by default all of these 4-byte spaces store the bits representing 0's. Then, you pushed 5 more ints (-1, -2, -3, -4, -5) onto the end of the vector with:
v.push_back(-1);
v.push_back(-2);
v.push_back(-3);
v.push_back(-4);
v.push_back(-5);
At this point the vector has 10 elements, the first five being the unknown ints that happen to store 0's on the instance you ran the program. Since your for loop prints the first five elements in the vector, this is why it printed all 0's.
When you declared this line vector<int>v(5) it created a vector of size 5 with default value of all elements 0, at this stage your vector looks like this -
{0, 0, 0, 0, 0}
Now when you called v.push_back(-1) what it did is it pushed -1 to the back of vector increasing its size, now your vector looks like this -
{0, 0, 0, 0, 0, -1}
After every push back is performed your final vector will be - {0, 0, 0, 0, 0, -1, -2, -3, -4, -5}
The size of your vector is now increased to 10 from 5 and when you looped from index 0 to 4 it only printed 0. I hope I explained it well and fix for this is already provided in previous answers.

mapping 1D array with two points

I want to store random snapshots of a a 1D array arr using a mapping of 2 points say a,b to utilise in a dynamic program. like say
P1 => (4,5) = [1,1,0,2,1]
P2 => (10,13) = [5,6,3,4,3]
P3 => (15,23) = [11,13,9,12,14]
so on.....
Later I need to add them to array A having also n elements like arr.
like say A = [1,1,1,1,1]
so now after P1 i have A as : [2,2,1,3,2] and so on till P ends.
I am not sure how to map these points P1,P2 ... with the array arr and then later sum it with array A.
I tried using a 3D array to store those points and then the array.But it seems bulky and not utilised in an efficient way.
Any help is appreciated.
You can use a
std::map<std::pair<int, int>, std::vector<int>> m;
then you can use it as m[{10, 20}].push_back(42);.
The data in the question for example could be stored with
m[{4, 5}] = {1, 1, 0, 2, 1};
m[{10, 13}] = {5, 6, 3, 4, 3};
m[{15, 23}] = {11, 13, 9, 12, 14};
According to this answer posted by GManNickG https://stackoverflow.com/a/2197015/7943474, un unordered_map will use more memory but in your case it can be much faster since you are not going to add/delete points once you initialize your set of data.
In this case you should consider:
#include <unordered_map>
std::unordered_map<std::pair<int, int>, std::vector<int>> uMap;
and then insert your element as:
std::pair<int, int> point(4, 5);
std::vector<int> arr;
arr.push_back(1);
arr.push_back(1);
and so on..
uMap.insert(point, arr);
To find and element in the uMap, you can use the find() method:
std::unordered_map<std::pair<int, int>, std::vector<int>>::const_iterator it = uMap.find(point);
and then update data with
it->second[0] + A[0];
it->second[1] + A[1];
and so on..
For more reference see http://www.cplusplus.com/reference/unordered_map/unordered_map/