I'm trying to set the values of an arma::mat element-wise and the value of each element depends on the multi-index (row, column) of each element.
Is there a way to retrieve the current location of an element during iteration?
Basically, I'd like to be able to do something like in the sparse matrix iterator, where it.col() and it.row() allow to retrieve the current element's location.
For illustration, the example given in the arma::sp_mat iterator documentation) is:
sp_mat X = sprandu<sp_mat>(1000, 2000, 0.1);
sp_mat::const_iterator it = X.begin();
sp_mat::const_iterator it_end = X.end();
for (; it != it_end; ++it) {
cout << "val: " << (*it) << endl;
cout << "row: " << it.row() << endl; // only available for arma::sp_mat, not arma::mat
cout << "col: " << it.col() << endl; // only available for arma::sp_mat, not arma::mat
}
Of course there are a number of workarounds to get element locations for arma::mat iteration, the most straight-forward ones perhaps being:
Use nested for-loops over the row and column sizes.
Use a single for loop and, using the matrix size, transform the iteration number to a row and column index.
Some form of "zipped" iteration with an object containing or computing the corresponding indices.
However, these seem rather hacky and error-prone to me, because they require to work with the matrix sizes or even do manual index juggling.
I'm looking for a cleaner (and perhaps internally optimised) solution. It feels to me like there should be a way to achieve this ...
Apart from the solution used for arma::sp_mat, other such "nice" solution for me would be using .imbue or .for_each but with a functor that accepts not only the element's current value but also its location as additional argument; this doesn't seem to be possible currently.
Looking at the armadillo source code, row_col_iterator provides row and column indices of each element. This works like the sparse matrix iterator, but doesn't skip zeros. Adapting your code:
mat X(10,10,fill::randu);
mat::const_row_col_iterator it = X.begin_row_col();
mat::const_row_col_iterator it_end = X.end_row_col();
for (; it != it_end; ++it) {
cout << "val: " << (*it) << endl;
cout << "row: " << it.row() << endl;
cout << "col: " << it.col() << endl;
}
You seem to have already answered your question yourself. I wish armadillo provided us with a .imbue method overload that received a functor with as many arguments as the dimension of the armadillo object, but currently it only accepts a functor without arguments. Then the probably simplest option (in my opinion) is using a lambda capturing the necessary information, such as the code below
arma::umat m(3, 3);
{
int i = 0;
m.imbue([&i, num_rows = m.n_rows, num_cols = m.n_cols]() {
arma::uvec sub = arma::ind2sub(arma::SizeMat{num_rows, num_cols}, i++);
return 10 * (sub[0] + 1) + sub[1];
});
}
In this example each element is computed as 10 times its row index plus its column index. I capture ì here to be the linear indexing and put it as well as the lambda inside curly brackets to delimit its scope.
I also wish I could write something like auto [row_idx, col_idx] = arma::ind2sub( ... ), but unfortunately what ind2sub returns does not work with structured binding.
You can also capture m by const reference and use arma::size(m) as the first argument of arma::ind2sub, if you prefer.
Related
I'm trying to use the map to arrange my output, which is a password generated from the name and ic number, for each of the students. I wanted the output to be arranged in a numbering list. After I searched online, I found that it can be done using index or int. The class I'm taking is about Data Structures and it leans heavily towards how efficient the program can be. I've already managed to get the output I want but I want to ask which way is more efficient or the better way to do it. Sorry if you don't understand what I'm saying, my explanation skills are quite terrible. If there's any improvement that can be made do let me know. TQVM.
By int:
void displayStudent(map<string, string>& studentMap) {
map<string, string>::iterator displayStudentAndPassword = studentMap.begin();
int i = 1;
for (displayStudentAndPassword = studentMap.begin(); displayStudentAndPassword != studentMap.end(); displayStudentAndPassword++) {
cout << i << ". " << displayStudentAndPassword->first << ":- " << displayStudentAndPassword->second << endl;
cout << endl;
i++;
}
}
By index:
void displayStudent(map<string, string>& studentMap) {
map<string, string>::iterator displayStudentAndPassword = studentMap.begin();
int index = 1;
for (displayStudentAndPassword = studentMap.begin(); displayStudentAndPassword != studentMap.end(); displayStudentAndPassword++) {
cout << index << ". " << displayStudentAndPassword->first << ":- " << displayStudentAndPassword->second << endl;
cout << endl;
index++;
}
}
Output Image
As far as I can tell, the two solutions are identical. The only difference is that you have named your variable for tracking the index number differently. You named it "i" in one "index" in another. In this case you aren't using it as an index. You're just numbering your output. By using a map, there is no index number. Instead you have a key which is the first string in std::map<string, string>.
Maps are great for quickly mapping from one object to another. In this case from one string to another. Presumably so you can map from a student name to their stored password. However, if your code elsewhere doesn't need this, then I suggest using a vector instead of a map. Since it is fundamentally an array of objects referenced by index. Of course the object contained in the vector would then have to be an class or structure to hold both values, the name and password.
I'm attempting to iterate through a list of 6 'Chess' pieces. Each round they move a random amount and if they land on another then they 'kill' it.
The problem is that when the last piece in my vector kills another piece I'm getting a vector 'out of range' error. I'm guessing it's because I'm iterating through a vector whilst also removing items from it, but I'm not increasing the count when I erase a piece so I'm not entirely sure. Any help would be greatly appreciated.
Here is my vector:
vector<Piece*> pieces;
pieces.push_back(&b);
pieces.push_back(&r);
pieces.push_back(&q);
pieces.push_back(&b2);
pieces.push_back(&r2);
pieces.push_back(&q2);
and this is the loop I iterate using:
while (pieces.size() > 1) {
cout << "-------------- Round " << round << " --------------" << endl;
round++;
cout << pieces.size() << " pieces left" << endl;
i = 0;
while (i < pieces.size()) {
pieces.at(i)->move(board.getMaxLength());
j = 0;
while (j < pieces.size()) {
if (pieces.at(i) != pieces.at(j) && col.detectCollision(pieces.at(i), pieces.at(j))) {
cout << pieces.at(i)->getName() << " has slain " << pieces.at(j)->getName() << endl << endl;
pieces.at(i)->setKills(pieces.at(i)->getKills() + 1);
pieces.erase(pieces.begin() + j);
}
else {
j++;
}
}
i++;
}
}
Solution
pieces.erase(pieces.begin() + j);
break;
Your logic needs a little refinement.
The way you coded it, it seems the "turn based" nature of chess has been replaced by a kind of "priority list" -- the pieces closer to the start of the vector are allowed to move first and, thus, get the priority into smashing other pieces.
I don't know if you want this logic to be right or wrong. Anyway, the trouble seems to be due to unconditionally executing the line
i++;
It should not be executed if you remove a piece for the same reason 'j++' isn't executed: you will jump over a piece.
I have a very strong feeling that this line of code:
i++;
is your culprit which is missing either a needed break condition or another conditional check that is missing from your loops. As it pertains to your nested while loops' conditions since they are based on the current size of your vector and are not being updated accordingly.
while (pieces.size() > 1) {
// ...
while (i < pieces.size()) {
// ...
while (j < pieces.size()) {
// ...
}
}
}
This is due to the fact that you are calling this within the inner most nested loop:
pieces.erase(pieces.begin() + j);
You are inside of a nested while loop and if a certain condition is met you are then erasing the object at this index location within your vector while you are still inside of the inner while loop that you never break from or check to see if the index is still valid.
Initially you are entering this while loop with a vector that has 6 entries, and you call erase on it within the nested loop and now your vector has 5 entries.
This can reek havoc on your loops because your index counters i & j were set according to the original length of your vector with a size of 6, but now the vector has been reduced to a size of 5 while you are still within the inner most nested loop that you never break from nor check to see if the indices are valid. On the next iteration these values are now invalidated as you never break out of the loops to reset the indices according to the new size of your vector, nor check to see if they are valid.
Try running this simple program that will demonstrate what I mean by the indices being invalidated within your nested loops.
int main() {
std::vector<std::string> words{ "please", "erase", "me" };
std::cout << "Original Size: " << words.size() << '\n';
for (auto& s : words)
std::cout << s << " ";
std::cout << '\n';
words.erase(words.begin() + 2);
std::cout << "New Size: " << words.size() << '\n';
for (auto& s : words)
std::cout << s << " ";
std::cout << '\n';
return 0;
}
-Output-
Original Size: 3
please erase me
New Size: 2
please erase
You should save locally pieces.at(i) and use this local variable everywhere you use pieces.at(i).
To avoid both elements out of bound and logical problems you can use std::list.
As aside, you should use std::vector<Piece*> only if these are non-owning pointers, otherwise you should use smart-pointers, probably unique_ptr.
I wanted to distinguish between the end of an array an the rest of the elements (in a for loop), but most examples initializes a variable outside the loop, which I think clutters the loop. The shortest example I have achieved is by looking at pointer addresses in a ranged-based for-loop:
for(auto& x : arr){
cout << x;
if(&x != &*end(arr)-1)
cout << ", ";
}
This doesn't need an extra variable, but I am not 100% sure of the implications from using pointers in C++.
A more (or less?) readable example where I initialize a variable in the for-statement, in a way that looks quite intuitive (edit doesn't give portability to fuctions):
for(int i{0}, len{sizeof(arr)/sizeof(*arr)}; i<l; i++){
cout << arr[i];
if(i!=len-1)
cout << ", ";
}
Is there a more readable/better/shorter way to do this without extra includes?
Are there any cons to these approaches?
Why not do the following?
bool not_first_item = false;
for(auto x : arr){
if (not_first_item) {
cout << ", ";
not_first_item = true;
}
cout << x;
}
It will print a comma before each item except the first one. It will get the result you require without the need of using complicated pointers.
If all you have is a pointer to an element in an array, there is no portable way of detecting the position of that element in an array.
Alternatives; best first:
Use a std::vector. That has similar semantics as a plain old array and has the benefit of carrying in the size.
Pass the size of the array as an additional parameter, with size_t type.
Use a magic value to signify the end of an array.
Note that using &x is pointless as x is a value copy. Consider auto& x instead?
This might help
l=sizeof(arr)
for(int i{0}; i<l-1; i++){
cout << arr[i];
cout << ", ";
}
cout << arr[sizeof(arr)];
or
for(int i{0}; i<sizeof(arr)-1; i++){
cout << arr[i];
cout << ", ";
}
cout << arr[sizeof(arr)];
there is no extra condition.
If showing is the main intention
A concise way that I like, without extra branch:
const char* sep = "";
for (const auto& x : arr) {
std::cout << sep << x;
sep = ", ";
}
Yes it uses extra variable.
As a general rule, you should not discard information that you need. When you use a range-based for loop you abstract away the position of an element in the container (and in fact the form of the container). Therefore, it is not the most appropriate tool for the job. You could do this with an index or an iterator because those hold enough information for you to tell whether the element you are iterating over is the last one.
The standard library offers two helpful functions begin and end that either call the member functions begin and end of STL containers, or pointers to the first and past-the-end elements for C-style arrays. Because of the way the end condition is checked, you don't need anything more than a forward iterator.
assert(std::begin(arr) != std::end(arr));
for (auto it = std::begin(arr); it + 1 != std::end(arr); ++it) {
std::cout << *it << ", ";
}
std::cout << *(std::end(arr) - 1) << '\n';
The above code is fine if you know that you're never going to attempt to print an empty container. Otherwise, you'll need an extra if statement to check for that. Note that even if you have a random access iterator and you use the condition it < std::end(arr) - 1 you might reason that it's fine even for empty arrays, but it is undefined behavior and it might lead to some unexpected bugs when optimizations are turned on.
Not 100% sure what I was looking for but I think that the real answer might be to check for the starting item like #ed_heel proposed, but I really only needed to change what I checked for in the range-based for-loop to make it much neater:
for(auto &x : arr)
{
cout << (&x == arr ? "" : ", ") << x;
}
works since arr is an array (a.k.a. pointer in disguise).
I have 4 vectors with about 45,000 records each right now. Looking for an efficient method to run through these 4 vectors and output how many times it matches the users input. Data needs to match on the same index of each vector.
Multiple for loops? Vector find?
Thanks!
If the elements need to match at the same location, it seems that a std::find() or std::find_if() combined with a check for the other vectors at the position is a reasonable approach:
std::vector<A> a(...);
std::vector<B> b(...);
std::vector<C> c(...);
std::vector<D> d(...);
std::size_t match(0);
for (auto it = a.begin(), end = a.end(); it != end; ) {
it = std::find_if(it, end, conditionA));
if (it != end) {
if (conditionB[it - a.begin()]
&& conditionC[it - a.begin()]
&& conditionD[it - a.begin()]) {
++match;
}
++it;
}
}
What I got from description is that, you have 4 vectors and lots of user data, you need to find out how many of times it matches with vectors at same index
so here goes the code ( i am writing a c++4.3.2 code)
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
vector<typeT>a;
vector<typeT>b;
vector<typeT>c;
vector<typeT>d;
vector<typeT>matched;
/*i am assuming you have initialized a,b,c and d;
now we are going to do pre-calculation for matching user data and store
that in vector matched */
int minsize=min(a.size(),b.size(),c.size(),d.size());
for(int i=0;i<minsize;i++)
{
if(a[i]==b[i]&&b[i]==c[i]&&c[i]==d[i])matched.push_back(a[i]);
}
return 0;
}
this was the precalculation part. now next depend on data type you are using, Use binary search with little bit of extra counting or using a better data structure which stores a pair(value,recurrence) and then applying binary search.
Time complexity will be O(n+n*log(n)+m*log(n)) where n is minsize in code and m is number of user input
Honestly, I would have a couple of methods to maintain your database(vectors).
Essentially, do a QuickSort to start out with.
Then ever so often consistently run a insertion sort (Faster then QuickSort for partially sorted lists)
Then just run binary search on those vectors.
edit:
I think a better way to store this is instead of using multiple vectors per entry. Have one class vector that stores all the values. (your current vectors)
class entry {
public:
variable data1;
variable data2;
variable data3;
variable data4;
}
Make this into a single vector. Then use my method I described above to sort through these vectors.
You will have to sort through by what type of data it is first. Then after call binary search on that data.
You can create a lookup table for the vector with std::unordered_multimap in O(n). Then you can use unordered_multimap::count() to get the number of times the item appears in the vector and unordered_multimap::equal_range() to get the indices of the items inside your vector.
std::vector<std::string> a = {"ab", "ba", "ca", "ab", "bc", "ba"};
std::vector<std::string> b = {"fg", "fg", "ba", "eg", "gf", "ge"};
std::vector<std::string> c = {"pq", "qa", "ba", "fg", "de", "gf"};
std::unordered_multimap<std::string,int> lookup_table;
for (int i = 0; i < a.size(); i++) {
lookup_table.insert(std::make_pair(a[i], i));
lookup_table.insert(std::make_pair(b[i], i));
lookup_table.insert(std::make_pair(c[i], i));
}
// count
std::string userinput;
std::cin >> userinput;
int count = lookup_table.count(userinput);
std::cout << userinput << " shows up " << count << " times" << std::endl;
// print all the places where the key shows up
auto range = lookup_table.equal_range(userinput);
for (auto it = range.first; it != range.second; it++) {
int ind = it->second;
std::cout << " " << it->second << " "
<< a[ind] << " "
<< b[ind] << " "
<< c[ind] << std::endl;
}
This will be the most efficient if you will be searching the lookup table many items. If you only need to search one time, then Dietmar Kühl's approach would be most efficient.
I'm currently trying to display the total no. of topicids and testids based on the name.
However I'm having trouble doing that display. I initially had a vector containing all the data.
For e.g.
user1:name:topic1:test1
user1:name:topic2:test1
user2:name:topic1:test1
user2:name:topic2:test1
Due to the multiple duplicates in the vector, I want to display in the following format:
username:name:numofTopics:numofTests
user1:name:2:2
user1:name:2:2
Therefore, i thought of comparing the name against the next name in the vector and push in the element to a new vector called singleAcc. The purpose of this is to display the duplicate element as ONE element.
Below is my code for displaying the data
vector<AccDetails> singleAcc;
for (vector<AccDetails>::iterator itr=accInfo.begin();itr!=accInfo.end()-1; ++itr) {
if (itr->name == itr[1].name) {
//cout << (*itr) << endl;
singleAcc.push_back(*itr);
}
}
for (vector<AccDetails>::iterator itr = singleAcc();itr!=singleAcc();++itr) {
cout << left
<< setfill(' ')
<< setw(20) << itr[0].username
<< setw(20) << itr[0].name
<< setw(20) << countTopics(itr->name)
<< setw(20) << countTests()
<< endl;
}
Problem:
On the first vector iteration, the name will not compare against the last element bcoz of accDetails.end()-1.
How to display the duplicate elements as ONE element? Is what I'm doing in the 2nd iteration the right thing?
Hope someone can help me with this. Or is there a better way to doing this?
Thanks!
Why this won't work
Your proposed solution simply won't work as intended. Consider three elements that are considered duplicates in consecutive subsequence (I am using numbers to simplify the concept):
[1,1,1]
The iterator will first compare 1 to 1, and then push_back the first one.
Then it will compare second 1 to the third one, which again returns true, and the result that was supposed to have no duplicates ends up:
[1,1]
So it's clearly not something you want to do. In general, it looks like a rather weird problem, but so solve this one part you've posted here, I suggest using std::multiset.
A better solution
Create a comparator that tests for the name field just like you do here.
Then, recovering unique fields is rather simple:
std::multiset<AccDetail> s;
for (auto element_it = s.begin(); element_it != s.end(); element_it = s.upper_bound(*element_it)) {
auto er = s.equal_range(*element_it);
// use this to get all the elements with given name
for (auto i = er.first; i != er.second; ++i)
cout << *i << " ";
// use this to get the number of them
cout << std::distance(er.first, er.second);
}
See a working sample on Coliru.
Bonus
You are right that the iterator in first loop would go over the bounds. The solution for that is rather simple: zip iterator which can handle this automatically.
for (auto elem_pair : zip(v, v | drop(1)))
if (elem_pair.first == elem_pair.second)
...
Boost.Range has tools that allow that code to work. It would still suffer from the problems I've mentioned, though.